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