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