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