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