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