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