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