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