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