Major destabilisation, phase 2. This time it's the backends' turn:
[u/mdw/putty] / terminal.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4
5 #include <time.h>
6 #include <assert.h>
7 #include "putty.h"
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) )
21
22 #define VT52_PLUS
23
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 */
36
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)
45
46 #define compatibility(x) \
47 if ( ((CL_##x)&term->compatibility_level) == 0 ) { \
48 term->termstate=TOPLEVEL; \
49 break; \
50 }
51 #define compatibility2(x,y) \
52 if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \
53 term->termstate=TOPLEVEL; \
54 break; \
55 }
56
57 #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
58
59 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
60 const wchar_t sel_nl[] = SEL_NL;
61
62 /*
63 * Internal prototypes.
64 */
65 static void do_paint(Terminal *, Context, int);
66 static void erase_lots(Terminal *, int, int, int);
67 static void swap_screen(Terminal *, int, int, int);
68 static void update_sbar(Terminal *);
69 static void deselect(Terminal *);
70 static void term_print_finish(Terminal *);
71
72 /*
73 * Resize a line to make it `cols' columns wide.
74 */
75 unsigned 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 /*
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 */
102 unsigned long *lineptr(Terminal *term, int y, int lineno)
103 {
104 unsigned long *line, *newline;
105 tree234 *whichtree;
106 int treeindex;
107
108 if (y >= 0) {
109 whichtree = term->screen;
110 treeindex = y;
111 } else {
112 whichtree = term->scrollback;
113 treeindex = y + count234(term->scrollback);
114 }
115 line = index234(whichtree, treeindex);
116
117 /* We assume that we don't screw up and retrieve something out of range. */
118 assert(line != NULL);
119
120 newline = resizeline(line, term->cols);
121 if (newline != line) {
122 delpos234(whichtree, treeindex);
123 addpos234(whichtree, newline, treeindex);
124 line = newline;
125 }
126
127 return line + 1;
128 }
129
130 #define lineptr(x) lineptr(term,x,__LINE__)
131
132 /*
133 * Set up power-on settings for the terminal.
134 */
135 static void power_on(Terminal *term)
136 {
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;
143 else
144 term->alt_b = term->marg_b = 0;
145 if (term->cols != -1) {
146 int i;
147 for (i = 0; i < term->cols; i++)
148 term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
149 }
150 term->alt_om = term->dec_om = cfg.dec_om;
151 term->alt_ins = term->insert = FALSE;
152 term->alt_wnext = term->wrapnext = term->save_wnext = FALSE;
153 term->alt_wrap = term->wrap = cfg.wrap_mode;
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;
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);
172 {
173 int i;
174 for (i = 0; i < 256; i++)
175 term->wordness[i] = cfg.wordness[i];
176 }
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);
182 }
183 }
184
185 /*
186 * Force a screen update.
187 */
188 void term_update(Terminal *term)
189 {
190 Context ctx;
191 ctx = get_ctx();
192 if (ctx) {
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;
197 need_sbar_update = TRUE;
198 }
199 if (need_sbar_update)
200 update_sbar(term);
201 do_paint(term, ctx, TRUE);
202 sys_cursor(term->curs.x, term->curs.y - term->disptop);
203 free_ctx(ctx);
204 }
205 }
206
207 /*
208 * Called from front end when a keypress occurs, to trigger
209 * anything magical that needs to happen in that situation.
210 */
211 void term_seen_key_event(Terminal *term)
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 */
220 term->beep_overloaded = FALSE;
221 while (term->beephead) {
222 struct beeptime *tmp = term->beephead;
223 term->beephead = tmp->next;
224 sfree(tmp);
225 }
226 term->beeptail = NULL;
227 term->nbeeps = 0;
228
229 /*
230 * Reset the scrollback on keypress, if we're doing that.
231 */
232 if (cfg.scroll_on_key) {
233 term->disptop = 0; /* return to main screen */
234 term->seen_disp_event = 1;
235 }
236 }
237
238 /*
239 * Same as power_on(), but an external function.
240 */
241 void term_pwron(Terminal *term)
242 {
243 power_on(term);
244 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
245 fix_cpos;
246 term->disptop = 0;
247 deselect(term);
248 term_update(term);
249 }
250
251 /*
252 * When the user reconfigures us, we need to check the forbidden-
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.
256 */
257 void term_reconfig(Terminal *term)
258 {
259 if (cfg.no_alt_screen)
260 swap_screen(term, 0, FALSE, FALSE);
261 if (cfg.no_mouse_rep) {
262 term->xterm_mouse = 0;
263 set_raw_mouse_mode(0);
264 }
265 if (cfg.no_remote_charset) {
266 term->cset_attr[0] = term->cset_attr[1] = ATTR_ASCII;
267 term->sco_acs = term->alt_sco_acs = 0;
268 term->utf = 0;
269 }
270 if (!*cfg.printer) {
271 term_print_finish(term);
272 }
273 }
274
275 /*
276 * Clear the scrollback.
277 */
278 void term_clrsb(Terminal *term)
279 {
280 unsigned long *line;
281 term->disptop = 0;
282 while ((line = delpos234(term->scrollback, 0)) != NULL) {
283 sfree(line);
284 }
285 update_sbar(term);
286 }
287
288 /*
289 * Initialise the terminal.
290 */
291 Terminal *term_init(void)
292 {
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);
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;
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;
331 term->resize_fn = NULL;
332 term->resize_ctx = NULL;
333
334 return term;
335 }
336
337 /*
338 * Set up the terminal for a given size.
339 */
340 void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
341 {
342 tree234 *newalt;
343 unsigned long *newdisp, *line;
344 int i, j;
345 int sblen;
346 int save_alt_which = term->alt_which;
347
348 if (newrows == term->rows && newcols == term->cols &&
349 newsavelines == term->savelines)
350 return; /* nothing to do */
351
352 deselect(term);
353 swap_screen(term, 0, FALSE, FALSE);
354
355 term->alt_t = term->marg_t = 0;
356 term->alt_b = term->marg_b = newrows - 1;
357
358 if (term->rows == -1) {
359 term->scrollback = newtree234(NULL);
360 term->screen = newtree234(NULL);
361 term->rows = 0;
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 */
384 sblen = count234(term->scrollback);
385 /* Do this loop to expand the screen if newrows > rows */
386 for (i = term->rows; i < newrows; i++) {
387 if (sblen > 0) {
388 line = delpos234(term->scrollback, --sblen);
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 }
395 addpos234(term->screen, line, 0);
396 }
397 /* Do this loop to shrink the screen if newrows < rows */
398 for (i = newrows; i < term->rows; i++) {
399 line = delpos234(term->screen, 0);
400 addpos234(term->scrollback, line, sblen++);
401 }
402 assert(count234(term->screen) == newrows);
403 while (sblen > newsavelines) {
404 line = delpos234(term->scrollback, 0);
405 sfree(line);
406 sblen--;
407 }
408 assert(count234(term->scrollback) <= newsavelines);
409 term->disptop = 0;
410
411 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
412 for (i = 0; i < newrows * (newcols + 1); i++)
413 newdisp[i] = ATTR_INVALID;
414 sfree(term->disptext);
415 term->disptext = newdisp;
416 term->dispcurs = NULL;
417
418 newalt = newtree234(NULL);
419 for (i = 0; i < newrows; i++) {
420 line = smalloc(TSIZE * (newcols + 2));
421 line[0] = newcols;
422 for (j = 0; j <= newcols; j++)
423 line[j + 1] = term->erase_char;
424 addpos234(newalt, line, i);
425 }
426 if (term->alt_screen) {
427 while (NULL != (line = delpos234(term->alt_screen, 0)))
428 sfree(line);
429 freetree234(term->alt_screen);
430 }
431 term->alt_screen = newalt;
432
433 term->tabs = srealloc(term->tabs, newcols * sizeof(*term->tabs));
434 {
435 int i;
436 for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++)
437 term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
438 }
439
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;
454 fix_cpos;
455
456 swap_screen(term, save_alt_which, FALSE, FALSE);
457
458 update_sbar(term);
459 term_update(term);
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 */
468 void 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);
476 }
477
478 /*
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.)
484 */
485 static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)
486 {
487 int t;
488 tree234 *ttr;
489
490 if (!which)
491 reset = FALSE; /* do no weird resetting if which==0 */
492
493 if (which != term->alt_which) {
494 term->alt_which = which;
495
496 ttr = term->alt_screen;
497 term->alt_screen = term->screen;
498 term->screen = ttr;
499 t = term->curs.x;
500 if (!reset && !keep_cur_pos)
501 term->curs.x = term->alt_x;
502 term->alt_x = t;
503 t = term->curs.y;
504 if (!reset && !keep_cur_pos)
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;
534 }
535
536 if (reset && term->screen) {
537 /*
538 * Yes, this _is_ supposed to honour background-colour-erase.
539 */
540 erase_lots(term, FALSE, TRUE, TRUE);
541 }
542
543 /*
544 * This might not be possible if we're called during
545 * initialisation.
546 */
547 if (term->screen)
548 fix_cpos;
549 }
550
551 /*
552 * Update the scroll bar.
553 */
554 static void update_sbar(Terminal *term)
555 {
556 int nscroll;
557
558 nscroll = count234(term->scrollback);
559
560 set_sbar(nscroll + term->rows, nscroll + term->disptop, term->rows);
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 */
567 static void check_selection(Terminal *term, pos from, pos to)
568 {
569 if (poslt(from, term->selend) && poslt(term->selstart, to))
570 deselect(term);
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.
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.
582 */
583 static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
584 {
585 unsigned long *line, *line2;
586 int i, seltop;
587
588 if (topline != 0 || term->alt_which != 0)
589 sb = FALSE;
590
591 if (lines < 0) {
592 while (lines < 0) {
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;
605 }
606 }
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;
612 }
613 }
614
615 lines++;
616 }
617 } else {
618 while (lines > 0) {
619 line = delpos234(term->screen, topline);
620 if (sb && term->savelines > 0) {
621 int sblen = count234(term->scrollback);
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 */
628 if (sblen == term->savelines) {
629 sblen--, line2 = delpos234(term->scrollback, 0);
630 } else {
631 line2 = smalloc(TSIZE * (term->cols + 2));
632 line2[0] = term->cols;
633 }
634 addpos234(term->scrollback, line, sblen);
635 line = line2;
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 */
651 if (term->disptop > -term->savelines && term->disptop < 0)
652 term->disptop--;
653 }
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);
659
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.
666 *
667 * This applies to selstart and selend (for an existing
668 * selection), and also selanchor (for one being
669 * selected as we speak).
670 */
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;
679 }
680 }
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;
686 }
687 }
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;
693 }
694 }
695
696 lines--;
697 }
698 }
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 */
707 static void move(Terminal *term, int x, int y, int marg_clip)
708 {
709 if (x < 0)
710 x = 0;
711 if (x >= term->cols)
712 x = term->cols - 1;
713 if (marg_clip) {
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;
720 }
721 if (y < 0)
722 y = 0;
723 if (y >= term->rows)
724 y = term->rows - 1;
725 term->curs.x = x;
726 term->curs.y = y;
727 fix_cpos;
728 term->wrapnext = FALSE;
729 }
730
731 /*
732 * Save or restore the cursor and SGR mode.
733 */
734 static void save_cursor(Terminal *term, int save)
735 {
736 if (save) {
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;
744 } else {
745 term->curs = term->savecurs;
746 /* Make sure the window hasn't shrunk since the save */
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;
756 /*
757 * wrapnext might reset to False if the x position is no
758 * longer at the rightmost edge.
759 */
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;
764 fix_cpos;
765 if (term->use_bce)
766 term->erase_char = (' ' | ATTR_ASCII |
767 (term->curr_attr &
768 (ATTR_FGMASK | ATTR_BGMASK)));
769 }
770 }
771
772 /*
773 * Erase a large portion of the screen: the whole screen, or the
774 * whole line, or parts thereof.
775 */
776 static void erase_lots(Terminal *term,
777 int line_only, int from_begin, int to_end)
778 {
779 pos start, end;
780 int erase_lattr;
781 unsigned long *ldata;
782
783 if (line_only) {
784 start.y = term->curs.y;
785 start.x = 0;
786 end.y = term->curs.y + 1;
787 end.x = 0;
788 erase_lattr = FALSE;
789 } else {
790 start.y = 0;
791 start.x = 0;
792 end.y = term->rows;
793 end.x = 0;
794 erase_lattr = TRUE;
795 }
796 if (!from_begin) {
797 start = term->curs;
798 }
799 if (!to_end) {
800 end = term->curs;
801 incpos(end);
802 }
803 check_selection(term, start, end);
804
805 /* Clear screen also forces a full window redraw, just in case. */
806 if (start.y == 0 && start.x == 0 && end.y == term->rows)
807 term_invalidate(term);
808
809 ldata = lineptr(start.y);
810 while (poslt(start, end)) {
811 if (start.x == term->cols && !erase_lattr)
812 ldata[start.x] &= ~LATTR_WRAPPED;
813 else
814 ldata[start.x] = term->erase_char;
815 if (incpos(start) && start.y < term->rows)
816 ldata = lineptr(start.y);
817 }
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 */
824 static void insch(Terminal *term, int n)
825 {
826 int dir = (n < 0 ? -1 : +1);
827 int m;
828 pos cursplus;
829 unsigned long *ldata;
830
831 n = (n < 0 ? -n : n);
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);
839 if (dir < 0) {
840 memmove(ldata + term->curs.x, ldata + term->curs.x + n, m * TSIZE);
841 while (n--)
842 ldata[term->curs.x + m++] = term->erase_char;
843 } else {
844 memmove(ldata + term->curs.x + n, ldata + term->curs.x, m * TSIZE);
845 while (n--)
846 ldata[term->curs.x + n] = term->erase_char;
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 */
854 static void toggle_mode(Terminal *term, int mode, int query, int state)
855 {
856 unsigned long ticks;
857
858 if (query)
859 switch (mode) {
860 case 1: /* application cursor keys */
861 term->app_cursor_keys = state;
862 break;
863 case 2: /* VT52 mode */
864 term->vt52_mode = !state;
865 if (term->vt52_mode) {
866 term->blink_is_real = FALSE;
867 term->vt52_bold = FALSE;
868 } else {
869 term->blink_is_real = cfg.blinktext;
870 }
871 break;
872 case 3: /* 80/132 columns */
873 deselect(term);
874 if (!cfg.no_remote_resize)
875 request_resize(state ? 132 : 80, term->rows);
876 term->reset_132 = state;
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 */
885 ticks = GETTICKCOUNT();
886 /* turn off a previous vbell to avoid inconsistencies */
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*/
891 /* If there's no vbell timeout already, or this one lasts
892 * longer, replace vbell_timeout with ours. */
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) {
899 /* This is an ON, so we notice the time and save it. */
900 term->rvbell_startpoint = ticks;
901 }
902 term->rvideo = state;
903 term->seen_disp_event = TRUE;
904 if (state)
905 term_update(term);
906 break;
907 case 6: /* DEC origin mode */
908 term->dec_om = state;
909 break;
910 case 7: /* auto wrap */
911 term->wrap = state;
912 break;
913 case 8: /* auto key repeat */
914 term->repeat_off = !state;
915 break;
916 case 10: /* set local edit mode */
917 term->term_editing = state;
918 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
919 break;
920 case 25: /* enable/disable cursor */
921 compatibility2(OTHER, VT220);
922 term->cursor_on = state;
923 term->seen_disp_event = TRUE;
924 break;
925 case 47: /* alternate screen */
926 compatibility(OTHER);
927 deselect(term);
928 swap_screen(term, cfg.no_alt_screen ? 0 : state, FALSE, FALSE);
929 term->disptop = 0;
930 break;
931 case 1000: /* xterm mouse 1 */
932 term->xterm_mouse = state ? 1 : 0;
933 set_raw_mouse_mode(state);
934 break;
935 case 1002: /* xterm mouse 2 */
936 term->xterm_mouse = state ? 2 : 0;
937 set_raw_mouse_mode(state);
938 break;
939 case 1047: /* alternate screen */
940 compatibility(OTHER);
941 deselect(term);
942 swap_screen(term, cfg.no_alt_screen ? 0 : state, TRUE, TRUE);
943 term->disptop = 0;
944 break;
945 case 1048: /* save/restore cursor */
946 save_cursor(term, state);
947 if (!state) term->seen_disp_event = TRUE;
948 break;
949 case 1049: /* cursor & alternate screen */
950 if (state)
951 save_cursor(term, state);
952 if (!state) term->seen_disp_event = TRUE;
953 compatibility(OTHER);
954 deselect(term);
955 swap_screen(term, cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
956 if (!state)
957 save_cursor(term, state);
958 term->disptop = 0;
959 break;
960 } else
961 switch (mode) {
962 case 4: /* set insert mode */
963 compatibility(VT102);
964 term->insert = state;
965 break;
966 case 12: /* set echo mode */
967 term->term_echoing = !state;
968 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
969 break;
970 case 20: /* Return sends ... */
971 term->cr_lf_return = state;
972 break;
973 case 34: /* Make cursor BIG */
974 compatibility2(OTHER, VT220);
975 term->big_cursor = !state;
976 }
977 }
978
979 /*
980 * Process an OSC sequence: set window title or icon name.
981 */
982 static void do_osc(Terminal *term)
983 {
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];
988 } else {
989 term->osc_string[term->osc_strlen] = '\0';
990 switch (term->esc_args[0]) {
991 case 0:
992 case 1:
993 if (!cfg.no_remote_wintitle)
994 set_icon(term->osc_string);
995 if (term->esc_args[0] == 1)
996 break;
997 /* fall through: parameter 0 means set both */
998 case 2:
999 case 21:
1000 if (!cfg.no_remote_wintitle)
1001 set_title(term->osc_string);
1002 break;
1003 }
1004 }
1005 }
1006
1007 /*
1008 * ANSI printing routines.
1009 */
1010 static void term_print_setup(Terminal *term)
1011 {
1012 bufchain_clear(&term->printer_buf);
1013 term->print_job = printer_start_job(cfg.printer);
1014 }
1015 static void term_print_flush(Terminal *term)
1016 {
1017 void *data;
1018 int len;
1019 int size;
1020 while ((size = bufchain_size(&term->printer_buf)) > 5) {
1021 bufchain_prefix(&term->printer_buf, &data, &len);
1022 if (len > size-5)
1023 len = size-5;
1024 printer_job_data(term->print_job, data, len);
1025 bufchain_consume(&term->printer_buf, len);
1026 }
1027 }
1028 static void term_print_finish(Terminal *term)
1029 {
1030 void *data;
1031 int len, size;
1032 char c;
1033
1034 if (!term->printing && !term->only_printing)
1035 return; /* we need do nothing */
1036
1037 term_print_flush(term);
1038 while ((size = bufchain_size(&term->printer_buf)) > 0) {
1039 bufchain_prefix(&term->printer_buf, &data, &len);
1040 c = *(char *)data;
1041 if (c == '\033' || c == '\233') {
1042 bufchain_consume(&term->printer_buf, size);
1043 break;
1044 } else {
1045 printer_job_data(term->print_job, &c, 1);
1046 bufchain_consume(&term->printer_buf, 1);
1047 }
1048 }
1049 printer_finish_job(term->print_job);
1050 term->print_job = NULL;
1051 term->printing = term->only_printing = FALSE;
1052 }
1053
1054 /*
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 */
1059 void term_out(Terminal *term)
1060 {
1061 int c, unget;
1062 unsigned char localbuf[256], *chars;
1063 int nchars = 0;
1064
1065 unget = -1;
1066
1067 chars = NULL; /* placate compiler warnings */
1068 while (nchars > 0 || bufchain_size(&term->inbuf) > 0) {
1069 if (unget == -1) {
1070 if (nchars == 0) {
1071 void *ret;
1072 bufchain_prefix(&term->inbuf, &ret, &nchars);
1073 if (nchars > sizeof(localbuf))
1074 nchars = sizeof(localbuf);
1075 memcpy(localbuf, ret, nchars);
1076 bufchain_consume(&term->inbuf, nchars);
1077 chars = localbuf;
1078 assert(chars != NULL);
1079 }
1080 c = *chars++;
1081 nchars--;
1082
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)
1088 logtraffic((unsigned char) c, LGTYP_DEBUG);
1089 } else {
1090 c = unget;
1091 unget = -1;
1092 }
1093
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 */
1098
1099 /*
1100 * If we're printing, add the character to the printer
1101 * buffer.
1102 */
1103 if (term->printing) {
1104 bufchain_add(&term->printer_buf, &c, 1);
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 */
1111 if (term->only_printing) {
1112 if (c == '\033')
1113 term->print_state = 1;
1114 else if (c == (unsigned char)'\233')
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;
1122 else
1123 term->print_state = 0;
1124 if (term->print_state == 4) {
1125 term_print_finish(term);
1126 }
1127 continue;
1128 }
1129 }
1130
1131 /* First see about all those translations. */
1132 if (term->termstate == TOPLEVEL) {
1133 if (in_utf(term))
1134 switch (term->utf_state) {
1135 case 0:
1136 if (c < 0x80) {
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;
1142 } else if ((c & 0xe0) == 0xc0) {
1143 term->utf_size = term->utf_state = 1;
1144 term->utf_char = (c & 0x1f);
1145 } else if ((c & 0xf0) == 0xe0) {
1146 term->utf_size = term->utf_state = 2;
1147 term->utf_char = (c & 0x0f);
1148 } else if ((c & 0xf8) == 0xf0) {
1149 term->utf_size = term->utf_state = 3;
1150 term->utf_char = (c & 0x07);
1151 } else if ((c & 0xfc) == 0xf8) {
1152 term->utf_size = term->utf_state = 4;
1153 term->utf_char = (c & 0x03);
1154 } else if ((c & 0xfe) == 0xfc) {
1155 term->utf_size = term->utf_state = 5;
1156 term->utf_char = (c & 0x01);
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) {
1168 unget = c;
1169 c = UCSERR;
1170 term->utf_state = 0;
1171 break;
1172 }
1173 term->utf_char = (term->utf_char << 6) | (c & 0x3f);
1174 if (--term->utf_state)
1175 continue;
1176
1177 c = term->utf_char;
1178
1179 /* Is somebody trying to be evil! */
1180 if (c < 0x80 ||
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))
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;
1220 }
1221 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1222 else if(term->sco_acs &&
1223 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1224 {
1225 if (term->sco_acs == 2) c ^= 0x80;
1226 c |= ATTR_SCOACS;
1227 } else {
1228 switch (term->cset_attr[term->cset]) {
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;
1254 case ATTR_SCOACS:
1255 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1256 break;
1257 }
1258 }
1259 }
1260
1261 /* How about C1 controls ? */
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;
1266 c = '@' + (c & 0x1F);
1267 }
1268
1269 /* Or the GL control. */
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;
1274 fix_cpos;
1275 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1276 *term->cpos = (' ' | term->curr_attr | ATTR_ASCII);
1277 } else
1278 /* Or normal C0 controls. */
1279 if ((c & -32) == 0 && term->termstate < DO_CTRLS) {
1280 switch (c) {
1281 case '\005': /* terminal type query */
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 */
1290 compatibility(ANSIMIN);
1291 {
1292 char abuf[256], *s, *d;
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
1307 *d++ = *s;
1308 }
1309 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1310 }
1311 break;
1312 case '\007':
1313 {
1314 struct beeptime *newbeep;
1315 unsigned long ticks;
1316
1317 ticks = GETTICKCOUNT();
1318
1319 if (!term->beep_overloaded) {
1320 newbeep = smalloc(sizeof(struct beeptime));
1321 newbeep->ticks = ticks;
1322 newbeep->next = NULL;
1323 if (!term->beephead)
1324 term->beephead = newbeep;
1325 else
1326 term->beeptail->next = newbeep;
1327 term->beeptail = newbeep;
1328 term->nbeeps++;
1329 }
1330
1331 /*
1332 * Throw out any beeps that happened more than
1333 * t seconds ago.
1334 */
1335 while (term->beephead &&
1336 term->beephead->ticks < ticks - cfg.bellovl_t) {
1337 struct beeptime *tmp = term->beephead;
1338 term->beephead = tmp->next;
1339 sfree(tmp);
1340 if (!term->beephead)
1341 term->beeptail = NULL;
1342 term->nbeeps--;
1343 }
1344
1345 if (cfg.bellovl && term->beep_overloaded &&
1346 ticks - term->lastbeep >= (unsigned)cfg.bellovl_s) {
1347 /*
1348 * If we're currently overloaded and the
1349 * last beep was more than s seconds ago,
1350 * leave overload mode.
1351 */
1352 term->beep_overloaded = FALSE;
1353 } else if (cfg.bellovl && !term->beep_overloaded &&
1354 term->nbeeps >= cfg.bellovl_n) {
1355 /*
1356 * Now, if we have n or more beeps
1357 * remaining in the queue, go into overload
1358 * mode.
1359 */
1360 term->beep_overloaded = TRUE;
1361 }
1362 term->lastbeep = ticks;
1363
1364 /*
1365 * Perform an actual beep if we're not overloaded.
1366 */
1367 if (!cfg.bellovl || !term->beep_overloaded) {
1368 if (cfg.beep == BELL_VISUAL) {
1369 term->in_vbell = TRUE;
1370 term->vbell_startpoint = ticks;
1371 term_update(term);
1372 } else
1373 beep(cfg.beep);
1374 }
1375 term->disptop = 0;
1376 }
1377 break;
1378 case '\b':
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;
1386 else
1387 term->curs.x--;
1388 fix_cpos;
1389 term->seen_disp_event = TRUE;
1390 break;
1391 case '\016':
1392 compatibility(VT100);
1393 term->cset = 1;
1394 break;
1395 case '\017':
1396 compatibility(VT100);
1397 term->cset = 0;
1398 break;
1399 case '\033':
1400 if (term->vt52_mode)
1401 term->termstate = VT52_ESC;
1402 else {
1403 compatibility(ANSIMIN);
1404 term->termstate = SEEN_ESC;
1405 term->esc_query = FALSE;
1406 }
1407 break;
1408 case '\r':
1409 term->curs.x = 0;
1410 term->wrapnext = FALSE;
1411 fix_cpos;
1412 term->seen_disp_event = TRUE;
1413 term->paste_hold = 0;
1414 logtraffic((unsigned char) c, LGTYP_ASCII);
1415 break;
1416 case '\014':
1417 if (has_compat(SCOANSI)) {
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;
1423 break;
1424 }
1425 case '\013':
1426 compatibility(VT100);
1427 case '\n':
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++;
1432 if (cfg.lfhascr)
1433 term->curs.x = 0;
1434 fix_cpos;
1435 term->wrapnext = FALSE;
1436 term->seen_disp_event = 1;
1437 term->paste_hold = 0;
1438 logtraffic((unsigned char) c, LGTYP_ASCII);
1439 break;
1440 case '\t':
1441 {
1442 pos old_curs = term->curs;
1443 unsigned long *ldata = lineptr(term->curs.y);
1444
1445 do {
1446 term->curs.x++;
1447 } while (term->curs.x < term->cols - 1 &&
1448 !term->tabs[term->curs.x]);
1449
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;
1453 } else {
1454 if (term->curs.x >= term->cols)
1455 term->curs.x = term->cols - 1;
1456 }
1457
1458 fix_cpos;
1459 check_selection(term, old_curs, term->curs);
1460 }
1461 term->seen_disp_event = TRUE;
1462 break;
1463 }
1464 } else
1465 switch (term->termstate) {
1466 case TOPLEVEL:
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;
1476 fix_cpos;
1477 term->wrapnext = FALSE;
1478 }
1479 if (term->insert)
1480 insch(term, 1);
1481 if (term->selstate != NO_SELECTION) {
1482 pos cursplus = term->curs;
1483 incpos(cursplus);
1484 check_selection(term, term->curs, cursplus);
1485 }
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:
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;
1506 fix_cpos;
1507 }
1508 *term->cpos++ = UCSWIDE | term->curr_attr;
1509 break;
1510 case 1:
1511 *term->cpos++ = c | term->curr_attr;
1512 break;
1513 default:
1514 continue;
1515 }
1516 }
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;
1529 fix_cpos;
1530 term->wrapnext = FALSE;
1531 }
1532 }
1533 term->seen_disp_event = 1;
1534 break;
1535
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 == '\\') {
1543 do_osc(term);
1544 term->termstate = TOPLEVEL;
1545 break;
1546 }
1547 /* else fall through */
1548 case SEEN_ESC:
1549 if (c >= ' ' && c <= '/') {
1550 if (term->esc_query)
1551 term->esc_query = -1;
1552 else
1553 term->esc_query = c;
1554 break;
1555 }
1556 term->termstate = TOPLEVEL;
1557 switch (ANSI(c, term->esc_query)) {
1558 case '[': /* enter CSI mode */
1559 term->termstate = SEEN_CSI;
1560 term->esc_nargs = 1;
1561 term->esc_args[0] = ARG_DEFAULT;
1562 term->esc_query = FALSE;
1563 break;
1564 case ']': /* xterm escape sequences */
1565 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1566 compatibility(OTHER);
1567 term->termstate = SEEN_OSC;
1568 term->esc_args[0] = 0;
1569 break;
1570 case '7': /* save cursor */
1571 compatibility(VT100);
1572 save_cursor(term, TRUE);
1573 break;
1574 case '8': /* restore cursor */
1575 compatibility(VT100);
1576 save_cursor(term, FALSE);
1577 term->seen_disp_event = TRUE;
1578 break;
1579 case '=':
1580 compatibility(VT100);
1581 term->app_keypad_keys = TRUE;
1582 break;
1583 case '>':
1584 compatibility(VT100);
1585 term->app_keypad_keys = FALSE;
1586 break;
1587 case 'D': /* exactly equivalent to LF */
1588 compatibility(VT100);
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++;
1593 fix_cpos;
1594 term->wrapnext = FALSE;
1595 term->seen_disp_event = TRUE;
1596 break;
1597 case 'E': /* exactly equivalent to CR-LF */
1598 compatibility(VT100);
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++;
1604 fix_cpos;
1605 term->wrapnext = FALSE;
1606 term->seen_disp_event = TRUE;
1607 break;
1608 case 'M': /* reverse index - backwards LF */
1609 compatibility(VT100);
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--;
1614 fix_cpos;
1615 term->wrapnext = FALSE;
1616 term->seen_disp_event = TRUE;
1617 break;
1618 case 'Z': /* terminal type query */
1619 compatibility(VT100);
1620 ldisc_send(term->id_string, strlen(term->id_string), 0);
1621 break;
1622 case 'c': /* restore power-on settings */
1623 compatibility(VT100);
1624 power_on(term);
1625 ldisc_send(NULL, 0, 0);/* cause ldisc to notice changes */
1626 if (term->reset_132) {
1627 if (!cfg.no_remote_resize)
1628 request_resize(80, term->rows);
1629 term->reset_132 = 0;
1630 }
1631 fix_cpos;
1632 term->disptop = 0;
1633 term->seen_disp_event = TRUE;
1634 break;
1635 case 'H': /* set a tab */
1636 compatibility(VT100);
1637 term->tabs[term->curs.x] = TRUE;
1638 break;
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
1647 for (i = 0; i < term->rows; i++) {
1648 ldata = lineptr(i);
1649 for (j = 0; j < term->cols; j++)
1650 ldata[j] = ATTR_DEFAULT | 'E';
1651 ldata[term->cols] = 0;
1652 }
1653 term->disptop = 0;
1654 term->seen_disp_event = TRUE;
1655 scrtop.x = scrtop.y = 0;
1656 scrbot.x = 0;
1657 scrbot.y = term->rows;
1658 check_selection(term, scrtop, scrbot);
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;
1670 switch (ANSI(c, term->esc_query)) {
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;
1680 default: /* spiritually case ANSI('6', '#'): */
1681 nlattr = LATTR_WIDE;
1682 break;
1683 }
1684 ldata = lineptr(term->curs.y);
1685 ldata[term->cols] &= ~LATTR_MODE;
1686 ldata[term->cols] |= nlattr;
1687 }
1688 break;
1689
1690 case ANSI('A', '('):
1691 compatibility(VT100);
1692 if (!cfg.no_remote_charset)
1693 term->cset_attr[0] = ATTR_GBCHR;
1694 break;
1695 case ANSI('B', '('):
1696 compatibility(VT100);
1697 if (!cfg.no_remote_charset)
1698 term->cset_attr[0] = ATTR_ASCII;
1699 break;
1700 case ANSI('0', '('):
1701 compatibility(VT100);
1702 if (!cfg.no_remote_charset)
1703 term->cset_attr[0] = ATTR_LINEDRW;
1704 break;
1705 case ANSI('U', '('):
1706 compatibility(OTHER);
1707 if (!cfg.no_remote_charset)
1708 term->cset_attr[0] = ATTR_SCOACS;
1709 break;
1710
1711 case ANSI('A', ')'):
1712 compatibility(VT100);
1713 if (!cfg.no_remote_charset)
1714 term->cset_attr[1] = ATTR_GBCHR;
1715 break;
1716 case ANSI('B', ')'):
1717 compatibility(VT100);
1718 if (!cfg.no_remote_charset)
1719 term->cset_attr[1] = ATTR_ASCII;
1720 break;
1721 case ANSI('0', ')'):
1722 compatibility(VT100);
1723 if (!cfg.no_remote_charset)
1724 term->cset_attr[1] = ATTR_LINEDRW;
1725 break;
1726 case ANSI('U', ')'):
1727 compatibility(OTHER);
1728 if (!cfg.no_remote_charset)
1729 term->cset_attr[1] = ATTR_SCOACS;
1730 break;
1731
1732 case ANSI('8', '%'): /* Old Linux code */
1733 case ANSI('G', '%'):
1734 compatibility(OTHER);
1735 if (!cfg.no_remote_charset)
1736 term->utf = 1;
1737 break;
1738 case ANSI('@', '%'):
1739 compatibility(OTHER);
1740 if (!cfg.no_remote_charset)
1741 term->utf = 0;
1742 break;
1743 }
1744 break;
1745 case SEEN_CSI:
1746 term->termstate = TOPLEVEL; /* default */
1747 if (isdigit(c)) {
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';
1753 }
1754 term->termstate = SEEN_CSI;
1755 } else if (c == ';') {
1756 if (++term->esc_nargs <= ARGS_MAX)
1757 term->esc_args[term->esc_nargs - 1] = ARG_DEFAULT;
1758 term->termstate = SEEN_CSI;
1759 } else if (c < '@') {
1760 if (term->esc_query)
1761 term->esc_query = -1;
1762 else if (c == '?')
1763 term->esc_query = TRUE;
1764 else
1765 term->esc_query = c;
1766 term->termstate = SEEN_CSI;
1767 } else
1768 switch (ANSI(c, term->esc_query)) {
1769 case 'A': /* move up N lines */
1770 move(term, term->curs.x,
1771 term->curs.y - def(term->esc_args[0], 1), 1);
1772 term->seen_disp_event = TRUE;
1773 break;
1774 case 'e': /* move down N lines */
1775 compatibility(ANSI);
1776 /* FALLTHROUGH */
1777 case 'B':
1778 move(term, term->curs.x,
1779 term->curs.y + def(term->esc_args[0], 1), 1);
1780 term->seen_disp_event = TRUE;
1781 break;
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 */
1786 ldisc_send("\033[>0;136;0c", 11, 0);
1787 break;
1788 case 'a': /* move right N cols */
1789 compatibility(ANSI);
1790 /* FALLTHROUGH */
1791 case 'C':
1792 move(term, term->curs.x + def(term->esc_args[0], 1),
1793 term->curs.y, 1);
1794 term->seen_disp_event = TRUE;
1795 break;
1796 case 'D': /* move left N cols */
1797 move(term, term->curs.x - def(term->esc_args[0], 1),
1798 term->curs.y, 1);
1799 term->seen_disp_event = TRUE;
1800 break;
1801 case 'E': /* move down N lines and CR */
1802 compatibility(ANSI);
1803 move(term, 0,
1804 term->curs.y + def(term->esc_args[0], 1), 1);
1805 term->seen_disp_event = TRUE;
1806 break;
1807 case 'F': /* move up N lines and CR */
1808 compatibility(ANSI);
1809 move(term, 0,
1810 term->curs.y - def(term->esc_args[0], 1), 1);
1811 term->seen_disp_event = TRUE;
1812 break;
1813 case 'G':
1814 case '`': /* set horizontal posn */
1815 compatibility(ANSI);
1816 move(term, def(term->esc_args[0], 1) - 1,
1817 term->curs.y, 0);
1818 term->seen_disp_event = TRUE;
1819 break;
1820 case 'd': /* set vertical posn */
1821 compatibility(ANSI);
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;
1827 break;
1828 case 'H':
1829 case 'f': /* set horz and vert posns at once */
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;
1837 break;
1838 case 'J': /* erase screen or parts of it */
1839 {
1840 unsigned int i = def(term->esc_args[0], 0) + 1;
1841 if (i > 3)
1842 i = 0;
1843 erase_lots(term, FALSE, !!(i & 2), !!(i & 1));
1844 }
1845 term->disptop = 0;
1846 term->seen_disp_event = TRUE;
1847 break;
1848 case 'K': /* erase line or parts of it */
1849 {
1850 unsigned int i = def(term->esc_args[0], 0) + 1;
1851 if (i > 3)
1852 i = 0;
1853 erase_lots(term, TRUE, !!(i & 2), !!(i & 1));
1854 }
1855 term->seen_disp_event = TRUE;
1856 break;
1857 case 'L': /* insert lines */
1858 compatibility(VT102);
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);
1862 fix_cpos;
1863 term->seen_disp_event = TRUE;
1864 break;
1865 case 'M': /* delete lines */
1866 compatibility(VT102);
1867 if (term->curs.y <= term->marg_b)
1868 scroll(term, term->curs.y, term->marg_b,
1869 def(term->esc_args[0], 1),
1870 TRUE);
1871 fix_cpos;
1872 term->seen_disp_event = TRUE;
1873 break;
1874 case '@': /* insert chars */
1875 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1876 compatibility(VT102);
1877 insch(term, def(term->esc_args[0], 1));
1878 term->seen_disp_event = TRUE;
1879 break;
1880 case 'P': /* delete chars */
1881 compatibility(VT102);
1882 insch(term, -def(term->esc_args[0], 1));
1883 term->seen_disp_event = TRUE;
1884 break;
1885 case 'c': /* terminal type query */
1886 compatibility(VT100);
1887 /* This is the response for a VT102 */
1888 ldisc_send(term->id_string,
1889 strlen(term->id_string), 0);
1890 break;
1891 case 'n': /* cursor position query */
1892 if (term->esc_args[0] == 6) {
1893 char buf[32];
1894 sprintf(buf, "\033[%d;%dR", term->curs.y + 1,
1895 term->curs.x + 1);
1896 ldisc_send(buf, strlen(buf), 0);
1897 } else if (term->esc_args[0] == 5) {
1898 ldisc_send("\033[0n", 4, 0);
1899 }
1900 break;
1901 case 'h': /* toggle modes to high */
1902 case ANSI_QUE('h'):
1903 compatibility(VT100);
1904 {
1905 int i;
1906 for (i = 0; i < term->esc_nargs; i++)
1907 toggle_mode(term, term->esc_args[i],
1908 term->esc_query, TRUE);
1909 }
1910 break;
1911 case 'i':
1912 case ANSI_QUE('i'):
1913 compatibility(VT100);
1914 {
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);
1924 }
1925 }
1926 break;
1927 case 'l': /* toggle modes to low */
1928 case ANSI_QUE('l'):
1929 compatibility(VT100);
1930 {
1931 int i;
1932 for (i = 0; i < term->esc_nargs; i++)
1933 toggle_mode(term, term->esc_args[i],
1934 term->esc_query, FALSE);
1935 }
1936 break;
1937 case 'g': /* clear tabs */
1938 compatibility(VT100);
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) {
1943 int i;
1944 for (i = 0; i < term->cols; i++)
1945 term->tabs[i] = FALSE;
1946 }
1947 }
1948 break;
1949 case 'r': /* set scroll margins */
1950 compatibility(VT100);
1951 if (term->esc_nargs <= 2) {
1952 int top, bot;
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;
1960 /* VTTEST Bug 9 - if region is less than 2 lines
1961 * don't change region.
1962 */
1963 if (bot - top > 0) {
1964 term->marg_t = top;
1965 term->marg_b = bot;
1966 term->curs.x = 0;
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 *
1973 * Well actually it should for
1974 * Origin mode - RDB
1975 */
1976 term->curs.y = (term->dec_om ?
1977 term->marg_t : 0);
1978 fix_cpos;
1979 term->seen_disp_event = TRUE;
1980 }
1981 }
1982 break;
1983 case 'm': /* set graphics rendition */
1984 {
1985 /*
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.
1991 *
1992 * case 2:
1993 * This is sometimes DIM, eg on the
1994 * GIGI and Linux
1995 * case 8:
1996 * This is sometimes INVIS various ANSI.
1997 * case 21:
1998 * This like 22 disables BOLD, DIM and INVIS
1999 *
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.
2009 *
2010 * The 39 and 49 attributes are likely
2011 * to be unimplemented.
2012 */
2013 int i;
2014 for (i = 0; i < term->esc_nargs; i++) {
2015 switch (def(term->esc_args[i], 0)) {
2016 case 0: /* restore defaults */
2017 term->curr_attr = ATTR_DEFAULT;
2018 break;
2019 case 1: /* enable bold */
2020 compatibility(VT100AVO);
2021 term->curr_attr |= ATTR_BOLD;
2022 break;
2023 case 21: /* (enable double underline) */
2024 compatibility(OTHER);
2025 case 4: /* enable underline */
2026 compatibility(VT100AVO);
2027 term->curr_attr |= ATTR_UNDER;
2028 break;
2029 case 5: /* enable blink */
2030 compatibility(VT100AVO);
2031 term->curr_attr |= ATTR_BLINK;
2032 break;
2033 case 7: /* enable reverse video */
2034 term->curr_attr |= ATTR_REVERSE;
2035 break;
2036 case 10: /* SCO acs off */
2037 compatibility(SCOANSI);
2038 if (cfg.no_remote_charset) break;
2039 term->sco_acs = 0; break;
2040 case 11: /* SCO acs on */
2041 compatibility(SCOANSI);
2042 if (cfg.no_remote_charset) break;
2043 term->sco_acs = 1; break;
2044 case 12: /* SCO acs on flipped */
2045 compatibility(SCOANSI);
2046 if (cfg.no_remote_charset) break;
2047 term->sco_acs = 2; break;
2048 case 22: /* disable bold */
2049 compatibility2(OTHER, VT220);
2050 term->curr_attr &= ~ATTR_BOLD;
2051 break;
2052 case 24: /* disable underline */
2053 compatibility2(OTHER, VT220);
2054 term->curr_attr &= ~ATTR_UNDER;
2055 break;
2056 case 25: /* disable blink */
2057 compatibility2(OTHER, VT220);
2058 term->curr_attr &= ~ATTR_BLINK;
2059 break;
2060 case 27: /* disable reverse video */
2061 compatibility2(OTHER, VT220);
2062 term->curr_attr &= ~ATTR_REVERSE;
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 */
2073 term->curr_attr &= ~ATTR_FGMASK;
2074 term->curr_attr |=
2075 (term->esc_args[i] - 30)<<ATTR_FGSHIFT;
2076 break;
2077 case 39: /* default-foreground */
2078 term->curr_attr &= ~ATTR_FGMASK;
2079 term->curr_attr |= ATTR_DEFFG;
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 */
2090 term->curr_attr &= ~ATTR_BGMASK;
2091 term->curr_attr |=
2092 (term->esc_args[i] - 40)<<ATTR_BGSHIFT;
2093 break;
2094 case 49: /* default-background */
2095 term->curr_attr &= ~ATTR_BGMASK;
2096 term->curr_attr |= ATTR_DEFBG;
2097 break;
2098 }
2099 }
2100 if (term->use_bce)
2101 term->erase_char = (' ' | ATTR_ASCII |
2102 (term->curr_attr &
2103 (ATTR_FGMASK |
2104 ATTR_BGMASK)));
2105 }
2106 break;
2107 case 's': /* save cursor */
2108 save_cursor(term, TRUE);
2109 break;
2110 case 'u': /* restore cursor */
2111 save_cursor(term, FALSE);
2112 term->seen_disp_event = TRUE;
2113 break;
2114 case 't': /* set page size - ie window height */
2115 /*
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 */
2121 if (term->esc_nargs <= 1
2122 && (term->esc_args[0] < 1 ||
2123 term->esc_args[0] >= 24)) {
2124 compatibility(VT340TEXT);
2125 if (!cfg.no_remote_resize)
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) {
2132 compatibility(OTHER);
2133
2134 switch (term->esc_args[0]) {
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:
2144 if (term->esc_nargs >= 3) {
2145 if (!cfg.no_remote_resize)
2146 move_window(def(term->esc_args[1], 0),
2147 def(term->esc_args[2], 0));
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:
2166 if (term->esc_nargs >= 3) {
2167 if (!cfg.no_remote_resize)
2168 request_resize(def(term->esc_args[2], cfg.width),
2169 def(term->esc_args[1], cfg.height));
2170 }
2171 break;
2172 case 9:
2173 if (term->esc_nargs >= 2)
2174 set_zoomed(term->esc_args[1] ?
2175 TRUE : FALSE);
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",
2193 term->rows, term->cols);
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 }
2228 }
2229 break;
2230 case 'S':
2231 compatibility(SCOANSI);
2232 scroll(term, term->marg_t, term->marg_b,
2233 def(term->esc_args[0], 1), TRUE);
2234 fix_cpos;
2235 term->wrapnext = FALSE;
2236 term->seen_disp_event = TRUE;
2237 break;
2238 case 'T':
2239 compatibility(SCOANSI);
2240 scroll(term, term->marg_t, term->marg_b,
2241 -def(term->esc_args[0], 1), TRUE);
2242 fix_cpos;
2243 term->wrapnext = FALSE;
2244 term->seen_disp_event = TRUE;
2245 break;
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);
2253 if (term->esc_nargs == 1 && term->esc_args[0] > 0) {
2254 if (!cfg.no_remote_resize)
2255 request_resize(term->cols,
2256 def(term->esc_args[0],
2257 cfg.height));
2258 deselect(term);
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);
2267 if (term->esc_nargs <= 1) {
2268 if (!cfg.no_remote_resize)
2269 request_resize(def(term->esc_args[0],
2270 cfg.width), term->rows);
2271 deselect(term);
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 {
2278 int n = def(term->esc_args[0], 1);
2279 pos cursplus;
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;
2284 cursplus.x += n;
2285 check_selection(term, term->curs, cursplus);
2286 while (n--)
2287 *p++ = term->erase_char;
2288 term->seen_disp_event = TRUE;
2289 }
2290 break;
2291 case 'x': /* report terminal characteristics */
2292 compatibility(VT100);
2293 {
2294 char buf[32];
2295 int i = def(term->esc_args[0], 0);
2296 if (i == 0 || i == 1) {
2297 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2298 buf[2] += i;
2299 ldisc_send(buf, 20, 0);
2300 }
2301 }
2302 break;
2303 case 'Z': /* BackTab for xterm */
2304 compatibility(OTHER);
2305 {
2306 int i = def(term->esc_args[0], 1);
2307 pos old_curs = term->curs;
2308
2309 for(;i>0 && term->curs.x>0; i--) {
2310 do {
2311 term->curs.x--;
2312 } while (term->curs.x >0 &&
2313 !term->tabs[term->curs.x]);
2314 }
2315 fix_cpos;
2316 check_selection(term, old_curs, term->curs);
2317 }
2318 break;
2319 case ANSI('L', '='):
2320 compatibility(OTHER);
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)));
2327 break;
2328 case ANSI('E', '='):
2329 compatibility(OTHER);
2330 term->blink_is_real = (term->esc_args[0] >= 1);
2331 break;
2332 case ANSI('p', '"'):
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.
2338 *
2339 * The arg in 40..42,50 are a PuTTY extension.
2340 * The 2nd arg, 8bit vs 7bit is not checked.
2341 *
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.
2347 *
2348 * Note ESC c will NOT change this!
2349 */
2350
2351 switch (term->esc_args[0]) {
2352 case 61:
2353 term->compatibility_level &= ~TM_VTXXX;
2354 term->compatibility_level |= TM_VT102;
2355 break;
2356 case 62:
2357 term->compatibility_level &= ~TM_VTXXX;
2358 term->compatibility_level |= TM_VT220;
2359 break;
2360
2361 default:
2362 if (term->esc_args[0] > 60 &&
2363 term->esc_args[0] < 70)
2364 term->compatibility_level |= TM_VTXXX;
2365 break;
2366
2367 case 40:
2368 term->compatibility_level &= TM_VTXXX;
2369 break;
2370 case 41:
2371 term->compatibility_level = TM_PUTTY;
2372 break;
2373 case 42:
2374 term->compatibility_level = TM_SCOANSI;
2375 break;
2376
2377 case ARG_DEFAULT:
2378 term->compatibility_level = TM_PUTTY;
2379 break;
2380 case 50:
2381 break;
2382 }
2383
2384 /* Change the response to CSI c */
2385 if (term->esc_args[0] == 50) {
2386 int i;
2387 char lbuf[64];
2388 strcpy(term->id_string, "\033[?");
2389 for (i = 1; i < term->esc_nargs; i++) {
2390 if (i != 1)
2391 strcat(term->id_string, ";");
2392 sprintf(lbuf, "%d", term->esc_args[i]);
2393 strcat(term->id_string, lbuf);
2394 }
2395 strcat(term->id_string, "c");
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)) {
2402 if (!cfg.no_remote_resize) {
2403 if (term->reset_132)
2404 request_resize(132, 24);
2405 else
2406 request_resize(80, 24);
2407 }
2408 }
2409 #endif
2410 break;
2411 }
2412 break;
2413 case SEEN_OSC:
2414 term->osc_w = FALSE;
2415 switch (c) {
2416 case 'P': /* Linux palette sequence */
2417 term->termstate = SEEN_OSC_P;
2418 term->osc_strlen = 0;
2419 break;
2420 case 'R': /* Linux palette reset */
2421 palette_reset();
2422 term_invalidate(term);
2423 term->termstate = TOPLEVEL;
2424 break;
2425 case 'W': /* word-set */
2426 term->termstate = SEEN_OSC_W;
2427 term->osc_w = TRUE;
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':
2439 term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
2440 break;
2441 case 'L':
2442 /*
2443 * Grotty hack to support xterm and DECterm title
2444 * sequences concurrently.
2445 */
2446 if (term->esc_args[0] == 2) {
2447 term->esc_args[0] = 1;
2448 break;
2449 }
2450 /* else fall through */
2451 default:
2452 term->termstate = OSC_STRING;
2453 term->osc_strlen = 0;
2454 }
2455 break;
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
2466 */
2467 if (c == '\n' || c == '\r') {
2468 term->termstate = TOPLEVEL;
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 */
2477 do_osc(term);
2478 term->termstate = TOPLEVEL;
2479 } else if (c == '\033')
2480 term->termstate = OSC_MAYBE_ST;
2481 else if (term->osc_strlen < OSC_STR_MAX)
2482 term->osc_string[term->osc_strlen++] = c;
2483 break;
2484 case SEEN_OSC_P:
2485 {
2486 int max = (term->osc_strlen == 0 ? 21 : 16);
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;
2494 else {
2495 term->termstate = TOPLEVEL;
2496 break;
2497 }
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;
2506 }
2507 }
2508 break;
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':
2521 term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
2522 break;
2523 default:
2524 term->termstate = OSC_STRING;
2525 term->osc_strlen = 0;
2526 }
2527 break;
2528 case VT52_ESC:
2529 term->termstate = TOPLEVEL;
2530 term->seen_disp_event = TRUE;
2531 switch (c) {
2532 case 'A':
2533 move(term, term->curs.x, term->curs.y - 1, 1);
2534 break;
2535 case 'B':
2536 move(term, term->curs.x, term->curs.y + 1, 1);
2537 break;
2538 case 'C':
2539 move(term, term->curs.x + 1, term->curs.y, 1);
2540 break;
2541 case 'D':
2542 move(term, term->curs.x - 1, term->curs.y, 1);
2543 break;
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 */
2584 case 'F':
2585 term->cset_attr[term->cset = 0] = ATTR_LINEDRW;
2586 break;
2587 case 'G':
2588 term->cset_attr[term->cset = 0] = ATTR_ASCII;
2589 break;
2590 case 'H':
2591 move(term, 0, 0, 0);
2592 break;
2593 case 'I':
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--;
2598 fix_cpos;
2599 term->wrapnext = FALSE;
2600 break;
2601 case 'J':
2602 erase_lots(term, FALSE, FALSE, TRUE);
2603 term->disptop = 0;
2604 break;
2605 case 'K':
2606 erase_lots(term, TRUE, FALSE, TRUE);
2607 break;
2608 #if 0
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;
2618 #endif
2619 case 'Y':
2620 term->termstate = VT52_Y1;
2621 break;
2622 case 'Z':
2623 ldisc_send("\033/Z", 3, 0);
2624 break;
2625 case '=':
2626 term->app_keypad_keys = TRUE;
2627 break;
2628 case '>':
2629 term->app_keypad_keys = FALSE;
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 */
2636 term->vt52_mode = FALSE;
2637 term->blink_is_real = cfg.blinktext;
2638 break;
2639 #if 0
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;
2649 #endif
2650
2651 #ifdef VT52_PLUS
2652 case 'E':
2653 /* compatibility(ATARI) */
2654 move(term, 0, 0, 0);
2655 erase_lots(term, FALSE, FALSE, TRUE);
2656 term->disptop = 0;
2657 break;
2658 case 'L':
2659 /* compatibility(ATARI) */
2660 if (term->curs.y <= term->marg_b)
2661 scroll(term, term->curs.y, term->marg_b, -1, FALSE);
2662 break;
2663 case 'M':
2664 /* compatibility(ATARI) */
2665 if (term->curs.y <= term->marg_b)
2666 scroll(term, term->curs.y, term->marg_b, 1, TRUE);
2667 break;
2668 case 'b':
2669 /* compatibility(ATARI) */
2670 term->termstate = VT52_FG;
2671 break;
2672 case 'c':
2673 /* compatibility(ATARI) */
2674 term->termstate = VT52_BG;
2675 break;
2676 case 'd':
2677 /* compatibility(ATARI) */
2678 erase_lots(term, FALSE, TRUE, FALSE);
2679 term->disptop = 0;
2680 break;
2681 case 'e':
2682 /* compatibility(ATARI) */
2683 term->cursor_on = TRUE;
2684 break;
2685 case 'f':
2686 /* compatibility(ATARI) */
2687 term->cursor_on = FALSE;
2688 break;
2689 /* case 'j': Save cursor position - broken on ST */
2690 /* case 'k': Restore cursor position */
2691 case 'l':
2692 /* compatibility(ATARI) */
2693 erase_lots(term, TRUE, TRUE, TRUE);
2694 term->curs.x = 0;
2695 term->wrapnext = FALSE;
2696 fix_cpos;
2697 break;
2698 case 'o':
2699 /* compatibility(ATARI) */
2700 erase_lots(term, TRUE, TRUE, FALSE);
2701 break;
2702 case 'p':
2703 /* compatibility(ATARI) */
2704 term->curr_attr |= ATTR_REVERSE;
2705 break;
2706 case 'q':
2707 /* compatibility(ATARI) */
2708 term->curr_attr &= ~ATTR_REVERSE;
2709 break;
2710 case 'v': /* wrap Autowrap on - Wyse style */
2711 /* compatibility(ATARI) */
2712 term->wrap = 1;
2713 break;
2714 case 'w': /* Autowrap off */
2715 /* compatibility(ATARI) */
2716 term->wrap = 0;
2717 break;
2718
2719 case 'R':
2720 /* compatibility(OTHER) */
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)));
2727 break;
2728 case 'S':
2729 /* compatibility(VI50) */
2730 term->curr_attr |= ATTR_UNDER;
2731 break;
2732 case 'W':
2733 /* compatibility(VI50) */
2734 term->curr_attr &= ~ATTR_UNDER;
2735 break;
2736 case 'U':
2737 /* compatibility(VI50) */
2738 term->vt52_bold = TRUE;
2739 term->curr_attr |= ATTR_BOLD;
2740 break;
2741 case 'T':
2742 /* compatibility(VI50) */
2743 term->vt52_bold = FALSE;
2744 term->curr_attr &= ~ATTR_BOLD;
2745 break;
2746 #endif
2747 }
2748 break;
2749 case VT52_Y1:
2750 term->termstate = VT52_Y2;
2751 move(term, term->curs.x, c - ' ', 0);
2752 break;
2753 case VT52_Y2:
2754 term->termstate = TOPLEVEL;
2755 move(term, c - ' ', term->curs.y, 0);
2756 break;
2757
2758 #ifdef VT52_PLUS
2759 case VT52_FG:
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)));
2771 break;
2772 case VT52_BG:
2773 term->termstate = TOPLEVEL;
2774 term->curr_attr &= ~ATTR_BGMASK;
2775 term->curr_attr &= ~ATTR_BLINK;
2776 term->curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2777
2778 /* Note: bold background */
2779 if (c & 0x8)
2780 term->curr_attr |= ATTR_BLINK;
2781
2782 if (term->use_bce)
2783 term->erase_char = (' ' | ATTR_ASCII |
2784 (term->curr_attr &
2785 (ATTR_FGMASK | ATTR_BGMASK)));
2786 break;
2787 #endif
2788 default: break; /* placate gcc warning about enum use */
2789 }
2790 if (term->selstate != NO_SELECTION) {
2791 pos cursplus = term->curs;
2792 incpos(cursplus);
2793 check_selection(term, term->curs, cursplus);
2794 }
2795 }
2796
2797 term_print_flush(term);
2798 }
2799
2800 #if 0
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 */
2806 static int linecmp(Terminal *term, unsigned long *a, unsigned long *b)
2807 {
2808 int i, n;
2809
2810 for (i = n = 0; i < term->cols; i++)
2811 n += (*a++ == *b++);
2812 return n;
2813 }
2814 #endif
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 */
2820 static void do_paint(Terminal *term, Context ctx, int may_optimise)
2821 {
2822 int i, j, our_curs_y;
2823 unsigned long rv, cursor;
2824 pos scrpos;
2825 char ch[1024];
2826 long cursor_background = ERASE_CHAR;
2827 unsigned long ticks;
2828
2829 /*
2830 * Check the visual bell state.
2831 */
2832 if (term->in_vbell) {
2833 ticks = GETTICKCOUNT();
2834 if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
2835 term->in_vbell = FALSE;
2836 }
2837
2838 rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
2839
2840 /* Depends on:
2841 * screen array, disptop, scrtop,
2842 * selection, rv,
2843 * cfg.blinkpc, blink_is_real, tblinker,
2844 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2845 */
2846
2847 /* Has the cursor position or type changed ? */
2848 if (term->cursor_on) {
2849 if (term->has_focus) {
2850 if (term->blinker || !cfg.blink_cur)
2851 cursor = TATTR_ACTCURS;
2852 else
2853 cursor = 0;
2854 } else
2855 cursor = TATTR_PASCURS;
2856 if (term->wrapnext)
2857 cursor |= TATTR_RIGHTCURS;
2858 } else
2859 cursor = 0;
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;
2873 }
2874 term->dispcurs = NULL;
2875
2876 /* The normal screen data */
2877 for (i = 0; i < term->rows; i++) {
2878 unsigned long *ldata;
2879 int lattr;
2880 int idx, dirty_line, dirty_run, selected;
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
2887 scrpos.y = i + term->disptop;
2888 ldata = lineptr(scrpos.y);
2889 lattr = (ldata[term->cols] & LATTR_MODE);
2890
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];
2895
2896 for (j = 0; j < term->cols; j++, idx++) {
2897 unsigned long tattr, tchar;
2898 unsigned long *d = ldata + j;
2899 int break_run;
2900 scrpos.x = j;
2901
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;
2911 case ATTR_SCOACS:
2912 tchar = unitab_scoacs[tchar&0xFF];
2913 break;
2914 }
2915 tattr |= (tchar & CSET_MASK);
2916 tchar &= CHAR_MASK;
2917 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2918 tattr |= ATTR_WIDE;
2919
2920 /* Video reversing things */
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;
2930 tattr = (tattr ^ rv
2931 ^ (selected ? ATTR_REVERSE : 0));
2932
2933 /* 'Real' blinking ? */
2934 if (term->blink_is_real && (tattr & ATTR_BLINK)) {
2935 if (term->has_focus && term->tblinker) {
2936 tchar = ' ';
2937 tattr &= ~CSET_MASK;
2938 tattr |= ATTR_ACP;
2939 }
2940 tattr &= ~ATTR_BLINK;
2941 }
2942
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 */
2947 if ((tchar | tattr) != (term->disptext[idx]& ~ATTR_NARROW)) {
2948 if ((tattr & ATTR_WIDE) == 0 &&
2949 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2950 tattr |= ATTR_NARROW;
2951 } else if (term->disptext[idx]&ATTR_NARROW)
2952 tattr |= ATTR_NARROW;
2953
2954 /* Cursor here ? Save the 'background' */
2955 if (i == our_curs_y && j == term->curs.x) {
2956 cursor_background = tattr | tchar;
2957 term->dispcurs = term->disptext + idx;
2958 }
2959
2960 if ((term->disptext[idx] ^ tattr) & ATTR_WIDE)
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) {
2970 if ((tchar | tattr) == term->disptext[idx])
2971 break_run = TRUE;
2972 else if (!dirty_run && ccount == 1)
2973 break_run = TRUE;
2974 }
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
2989 if ((tchar | tattr) != term->disptext[idx])
2990 dirty_run = TRUE;
2991 ch[ccount++] = (char) tchar;
2992 term->disptext[idx] = tchar | tattr;
2993
2994 /* If it's a wide char step along to the next one. */
2995 if (tattr & ATTR_WIDE) {
2996 if (++j < term->cols) {
2997 idx++;
2998 d++;
2999 /* Cursor is here ? Ouch! */
3000 if (i == our_curs_y && j == term->curs.x) {
3001 cursor_background = *d;
3002 term->dispcurs = term->disptext + idx;
3003 }
3004 if (term->disptext[idx] != *d)
3005 dirty_run = TRUE;
3006 term->disptext[idx] = *d;
3007 }
3008 }
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) */
3016 if (i == our_curs_y && (term->curstype != cursor || updated_line)) {
3017 ch[0] = (char) (cursor_background & CHAR_MASK);
3018 attr = (cursor_background & ATTR_MASK) | cursor;
3019 do_cursor(ctx, term->curs.x, i, ch, 1, attr, lattr);
3020 term->curstype = cursor;
3021 }
3022 }
3023 }
3024
3025 /*
3026 * Flick the switch that says if blinking things should be shown or hidden.
3027 */
3028
3029 void term_blink(Terminal *term, int flg)
3030 {
3031 long now, blink_diff;
3032
3033 now = GETTICKCOUNT();
3034 blink_diff = now - term->last_tblink;
3035
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)) {
3038 term->last_tblink = now;
3039 term->tblinker = !term->tblinker;
3040 }
3041
3042 if (flg) {
3043 term->blinker = 1;
3044 term->last_blink = now;
3045 return;
3046 }
3047
3048 blink_diff = now - term->last_blink;
3049
3050 /* Make sure the cursor blinks no faster than system blink rate */
3051 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3052 return;
3053
3054 term->last_blink = now;
3055 term->blinker = !term->blinker;
3056 }
3057
3058 /*
3059 * Invalidate the whole screen so it will be repainted in full.
3060 */
3061 void term_invalidate(Terminal *term)
3062 {
3063 int i;
3064
3065 for (i = 0; i < term->rows * (term->cols + 1); i++)
3066 term->disptext[i] = ATTR_INVALID;
3067 }
3068
3069 /*
3070 * Paint the window in response to a WM_PAINT message.
3071 */
3072 void term_paint(Terminal *term, Context ctx,
3073 int left, int top, int right, int bottom)
3074 {
3075 int i, j;
3076 if (left < 0) left = 0;
3077 if (top < 0) top = 0;
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;
3086 else
3087 for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++)
3088 term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
3089 }
3090
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 ?
3094 */
3095 if (alt_pressed)
3096 do_paint (term, ctx, FALSE);
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 */
3106 void term_scroll(Terminal *term, int rel, int where)
3107 {
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);
3117 }
3118
3119 static void clipme(Terminal *term, pos top, pos bottom, int rect)
3120 {
3121 wchar_t *workbuf;
3122 wchar_t *wbptr; /* where next char goes within workbuf */
3123 int old_top_x;
3124 int wblen = 0; /* workbuf len */
3125 int buflen; /* amount of memory allocated to workbuf */
3126
3127 buflen = 5120; /* Default size */
3128 workbuf = smalloc(buflen * sizeof(wchar_t));
3129 wbptr = workbuf; /* start filling here */
3130 old_top_x = top.x; /* needed for rect==1 */
3131
3132 while (poslt(top, bottom)) {
3133 int nl = FALSE;
3134 unsigned long *ldata = lineptr(top.y);
3135 pos nlpos;
3136
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 */
3142 nlpos.y = top.y;
3143 nlpos.x = term->cols;
3144
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 */
3151 if (!(ldata[term->cols] & LATTR_WRAPPED)) {
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);
3157 if (poslt(nlpos, bottom))
3158 nl = TRUE;
3159 }
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
3174 while (poslt(top, bottom) && poslt(top, nlpos)) {
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;
3193 }
3194 case ATTR_ASCII:
3195 uc = unitab_line[uc & 0xFF];
3196 break;
3197 case ATTR_SCOACS:
3198 uc = unitab_scoacs[uc&0xFF];
3199 break;
3200 }
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;
3220 if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3221 buf[0] = c;
3222 buf[1] = (unsigned char) ldata[top.x + 1];
3223 rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3224 top.x++;
3225 } else {
3226 buf[0] = c;
3227 rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3228 }
3229
3230 if (rv > 0) {
3231 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3232 cbuf[rv] = 0;
3233 }
3234 }
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;
3248 }
3249 top.x++;
3250 }
3251 if (nl) {
3252 int i;
3253 for (i = 0; i < sel_nl_sz; i++) {
3254 wblen++;
3255 *wbptr++ = sel_nl[i];
3256 }
3257 }
3258 top.y++;
3259 top.x = rect ? old_top_x : 0;
3260 }
3261 #if SELECTION_NUL_TERMINATED
3262 wblen++;
3263 *wbptr++ = 0;
3264 #endif
3265 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3266 if (buflen > 0) /* indicates we allocated this buffer */
3267 sfree(workbuf);
3268 }
3269
3270 void term_copyall(Terminal *term)
3271 {
3272 pos top;
3273 top.y = -count234(term->scrollback);
3274 top.x = 0;
3275 clipme(term, top, term->curs, 0);
3276 }
3277
3278 /*
3279 * The wordness array is mainly for deciding the disposition of the
3280 * US-ASCII characters.
3281 */
3282 static int wordtype(Terminal *term, int uc)
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;
3360 case ATTR_SCOACS:
3361 uc = unitab_scoacs[uc&0xFF];
3362 break;
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
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
3379 if (uc < 0x80)
3380 return term->wordness[uc];
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;
3388 }
3389
3390 /*
3391 * Spread the selection outwards according to the selection mode.
3392 */
3393 static pos sel_spread_half(Terminal *term, pos p, int dir)
3394 {
3395 unsigned long *ldata;
3396 short wvalue;
3397 int topy = -count234(term->scrollback);
3398
3399 ldata = lineptr(p.y);
3400
3401 switch (term->selmode) {
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 */
3407 if (!(ldata[term->cols] & LATTR_WRAPPED)) {
3408 unsigned long *q = ldata + term->cols;
3409 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3410 q--;
3411 if (q == ldata + term->cols)
3412 q--;
3413 if (p.x >= q - ldata)
3414 p.x = (dir == -1 ? q - ldata : term->cols - 1);
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 */
3422 wvalue = wordtype(term, ldata[p.x]);
3423 if (dir == +1) {
3424 while (1) {
3425 if (p.x < term->cols-1) {
3426 if (wordtype(term, ldata[p.x + 1]) == wvalue)
3427 p.x++;
3428 else
3429 break;
3430 } else {
3431 if (ldata[term->cols] & LATTR_WRAPPED) {
3432 unsigned long *ldata2;
3433 ldata2 = lineptr(p.y+1);
3434 if (wordtype(term, ldata2[0]) == wvalue) {
3435 p.x = 0;
3436 p.y++;
3437 ldata = ldata2;
3438 } else
3439 break;
3440 } else
3441 break;
3442 }
3443 }
3444 } else {
3445 while (1) {
3446 if (p.x > 0) {
3447 if (wordtype(term, ldata[p.x - 1]) == wvalue)
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);
3456 if ((ldata2[term->cols] & LATTR_WRAPPED) &&
3457 wordtype(term, ldata2[term->cols-1]) == wvalue) {
3458 p.x = term->cols-1;
3459 p.y--;
3460 ldata = ldata2;
3461 } else
3462 break;
3463 }
3464 }
3465 }
3466 break;
3467 case SM_LINE:
3468 /*
3469 * In this mode, every line is a unit.
3470 */
3471 p.x = (dir == -1 ? 0 : term->cols - 1);
3472 break;
3473 }
3474 return p;
3475 }
3476
3477 static void sel_spread(Terminal *term)
3478 {
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);
3484 }
3485 }
3486
3487 void term_do_paste(Terminal *term)
3488 {
3489 wchar_t *data;
3490 int len;
3491
3492 get_clip(&data, &len);
3493 if (data && len > 0) {
3494 wchar_t *p, *q;
3495
3496 term_seen_key_event(term); /* pasted data counts */
3497
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));
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++) {
3513 term->paste_buffer[term->paste_len++] = q[i];
3514 }
3515 }
3516
3517 if (p <= data + len - sel_nl_sz &&
3518 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3519 term->paste_buffer[term->paste_len++] = '\r';
3520 p += sel_nl_sz;
3521 }
3522 q = p;
3523 }
3524
3525 /* Assume a small paste will be OK in one go. */
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;
3532 }
3533 }
3534 get_clip(NULL, NULL);
3535 }
3536
3537 void term_mouse(Terminal *term, Mouse_Button b, Mouse_Action a, int x, int y,
3538 int shift, int ctrl, int alt)
3539 {
3540 pos selpoint;
3541 unsigned long *ldata;
3542 int raw_mouse = (term->xterm_mouse &&
3543 !cfg.no_mouse_rep &&
3544 !(cfg.mouse_override && shift));
3545 int default_seltype;
3546
3547 if (y < 0) {
3548 y = 0;
3549 if (a == MA_DRAG && !raw_mouse)
3550 term_scroll(term, 0, -1);
3551 }
3552 if (y >= term->rows) {
3553 y = term->rows - 1;
3554 if (a == MA_DRAG && !raw_mouse)
3555 term_scroll(term, 0, +1);
3556 }
3557 if (x < 0) {
3558 if (y > 0) {
3559 x = term->cols - 1;
3560 y--;
3561 } else
3562 x = 0;
3563 }
3564 if (x >= term->cols)
3565 x = term->cols - 1;
3566
3567 selpoint.y = y + term->disptop;
3568 selpoint.x = x;
3569 ldata = lineptr(selpoint.y);
3570 if ((ldata[term->cols] & LATTR_MODE) != LATTR_NORM)
3571 selpoint.x /= 2;
3572
3573 if (raw_mouse) {
3574 int encstate = 0, r, c;
3575 char abuf[16];
3576 static int is_down = 0;
3577
3578 switch (b) {
3579 case MBT_LEFT:
3580 encstate = 0x20; /* left button down */
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;
3594 default: break; /* placate gcc warning about enum use */
3595 }
3596 switch (a) {
3597 case MA_DRAG:
3598 if (term->xterm_mouse == 1)
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;
3611 default: break; /* placate gcc warning about enum use */
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);
3621 ldisc_send(abuf, 6, 0);
3622 return;
3623 }
3624
3625 b = translate_button(b);
3626
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
3636 if (term->selstate == NO_SELECTION) {
3637 term->seltype = default_seltype;
3638 }
3639
3640 if (b == MBT_SELECT && a == MA_CLICK) {
3641 deselect(term);
3642 term->selstate = ABOUT_TO;
3643 term->seltype = default_seltype;
3644 term->selanchor = selpoint;
3645 term->selmode = SM_CHAR;
3646 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
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);
3654 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3655 (b == MBT_EXTEND && a != MA_RELEASE)) {
3656 if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
3657 return;
3658 if (b == MBT_EXTEND && a != MA_DRAG && term->selstate == SELECTED) {
3659 if (term->seltype == LEXICOGRAPHIC) {
3660 /*
3661 * For normal selection, we extend by moving
3662 * whichever end of the current selection is closer
3663 * to the mouse.
3664 */
3665 if (posdiff(selpoint, term->selstart) <
3666 posdiff(term->selend, term->selstart) / 2) {
3667 term->selanchor = term->selend;
3668 decpos(term->selanchor);
3669 } else {
3670 term->selanchor = term->selstart;
3671 }
3672 } else {
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 */
3678 if (2*selpoint.x < term->selstart.x + term->selend.x)
3679 term->selanchor.x = term->selend.x-1;
3680 else
3681 term->selanchor.x = term->selstart.x;
3682
3683 if (2*selpoint.y < term->selstart.y + term->selend.y)
3684 term->selanchor.y = term->selend.y;
3685 else
3686 term->selanchor.y = term->selstart.y;
3687 }
3688 term->selstate = DRAGGING;
3689 }
3690 if (term->selstate != ABOUT_TO && term->selstate != DRAGGING)
3691 term->selanchor = selpoint;
3692 term->selstate = DRAGGING;
3693 if (term->seltype == LEXICOGRAPHIC) {
3694 /*
3695 * For normal selection, we set (selstart,selend) to
3696 * (selpoint,selanchor) in some order.
3697 */
3698 if (poslt(selpoint, term->selanchor)) {
3699 term->selstart = selpoint;
3700 term->selend = term->selanchor;
3701 incpos(term->selend);
3702 } else {
3703 term->selstart = term->selanchor;
3704 term->selend = selpoint;
3705 incpos(term->selend);
3706 }
3707 } else {
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 */
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);
3717 }
3718 sel_spread(term);
3719 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3720 if (term->selstate == DRAGGING) {
3721 /*
3722 * We've completed a selection. We now transfer the
3723 * data to the clipboard.
3724 */
3725 clipme(term, term->selstart, term->selend,
3726 (term->seltype == RECTANGULAR));
3727 term->selstate = SELECTED;
3728 } else
3729 term->selstate = NO_SELECTION;
3730 } else if (b == MBT_PASTE
3731 && (a == MA_CLICK
3732 #if MULTICLICK_ONLY_EVENT
3733 || a == MA_2CLK || a == MA_3CLK
3734 #endif
3735 )) {
3736 request_paste();
3737 }
3738
3739 term_update(term);
3740 }
3741
3742 void term_nopaste(Terminal *term)
3743 {
3744 if (term->paste_len == 0)
3745 return;
3746 sfree(term->paste_buffer);
3747 term->paste_buffer = NULL;
3748 term->paste_len = 0;
3749 }
3750
3751 int term_paste_pending(Terminal *term)
3752 {
3753 return term->paste_len != 0;
3754 }
3755
3756 void term_paste(Terminal *term)
3757 {
3758 long now, paste_diff;
3759
3760 if (term->paste_len == 0)
3761 return;
3762
3763 /* Don't wait forever to paste */
3764 if (term->paste_hold) {
3765 now = GETTICKCOUNT();
3766 paste_diff = now - term->last_paste;
3767 if (paste_diff >= 0 && paste_diff < 450)
3768 return;
3769 }
3770 term->paste_hold = 0;
3771
3772 while (term->paste_pos < term->paste_len) {
3773 int n = 0;
3774 while (n + term->paste_pos < term->paste_len) {
3775 if (term->paste_buffer[term->paste_pos + n++] == '\r')
3776 break;
3777 }
3778 luni_send(term->paste_buffer + term->paste_pos, n, 0);
3779 term->paste_pos += n;
3780
3781 if (term->paste_pos < term->paste_len) {
3782 term->paste_hold = 1;
3783 return;
3784 }
3785 }
3786 sfree(term->paste_buffer);
3787 term->paste_buffer = NULL;
3788 term->paste_len = 0;
3789 }
3790
3791 static void deselect(Terminal *term)
3792 {
3793 term->selstate = NO_SELECTION;
3794 term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0;
3795 }
3796
3797 void term_deselect(Terminal *term)
3798 {
3799 deselect(term);
3800 term_update(term);
3801 }
3802
3803 int term_ldisc(Terminal *term, int option)
3804 {
3805 if (option == LD_ECHO)
3806 return term->term_echoing;
3807 if (option == LD_EDIT)
3808 return term->term_editing;
3809 return FALSE;
3810 }
3811
3812 /*
3813 * from_backend(), to get data from the backend for the terminal.
3814 */
3815 int from_backend(void *vterm, int is_stderr, char *data, int len)
3816 {
3817 Terminal *term = (Terminal *)vterm;
3818
3819 assert(len > 0);
3820
3821 bufchain_add(&term->inbuf, data, len);
3822
3823 /*
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 *
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.
3839 *
3840 * In practice, I can't imagine this causing serious trouble.
3841 */
3842 return 0;
3843 }