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