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 */
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)
34 #define TM_PUTTY (0xFFFF)
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
49 static int compatibility_level
= TM_PUTTY
;
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) */
56 static unsigned long *cpos
; /* cursor position (convenience) */
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 */
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
65 struct beeptime
*next
;
68 static struct beeptime
*beephead
, *beeptail
;
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
76 static unsigned long curr_attr
, save_attr
;
77 static unsigned long erase_char
= ERASE_CHAR
;
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) )
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. */
114 static int xterm_mouse
; /* send mouse messages to app */
116 static unsigned long cset_attr
[2];
119 * Saved settings on the alternate screen.
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
;
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)
134 #define OSC_STR_MAX 2048
135 static int osc_strlen
;
136 static char osc_string
[OSC_STR_MAX
+ 1];
139 static char id_string
[1024] = "\033[?6c";
141 static unsigned char *tabs
;
153 OSC_STRING
, OSC_MAYBE_ST
,
162 NO_SELECTION
, ABOUT_TO
, DRAGGING
, SELECTED
165 SM_CHAR
, SM_WORD
, SM_LINE
167 static pos selstart
, selend
, selanchor
;
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 */
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
;
194 * Internal prototypes.
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 static void xlatlognam(char *d
, char *s
, char *hostname
, struct tm
*tm
);
207 * Resize a line to make it `cols' columns wide.
209 unsigned long *resizeline(unsigned long *line
, int cols
)
212 unsigned long lineattrs
;
214 if (line
[0] != (unsigned long)cols
) {
216 * This line is the wrong length, which probably means it
217 * hasn't been accessed since a resize. Resize it now.
220 lineattrs
= line
[oldlen
+ 1];
221 line
= srealloc(line
, TSIZE
* (2 + cols
));
223 for (i
= oldlen
; i
< cols
; i
++)
224 line
[i
+ 1] = ERASE_CHAR
;
225 line
[cols
+ 1] = lineattrs
& LATTR_MODE
;
232 * Retrieve a line of the screen or of the scrollback, according to
233 * whether the y coordinate is non-negative or negative
236 unsigned long *lineptr(int y
, int lineno
)
238 unsigned long *line
, *newline
;
246 whichtree
= scrollback
;
247 treeindex
= y
+ count234(scrollback
);
249 line
= index234(whichtree
, treeindex
);
251 /* We assume that we don't screw up and retrieve something out of range. */
252 assert(line
!= NULL
);
254 newline
= resizeline(line
, cols
);
255 if (newline
!= line
) {
256 delpos234(whichtree
, treeindex
);
257 addpos234(whichtree
, newline
, treeindex
);
264 #define lineptr(x) lineptr(x,__LINE__)
266 * Set up power-on settings for the terminal.
268 static void power_on(void)
270 curs
.x
= curs
.y
= alt_x
= alt_y
= savecurs
.x
= savecurs
.y
= 0;
273 alt_b
= marg_b
= rows
- 1;
278 for (i
= 0; i
< cols
; i
++)
279 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
281 alt_om
= dec_om
= cfg
.dec_om
;
282 alt_wnext
= wrapnext
= alt_ins
= insert
= FALSE
;
283 alt_wrap
= wrap
= cfg
.wrap_mode
;
286 alt_sco_acs
= sco_acs
= 0;
287 cset_attr
[0] = cset_attr
[1] = ATTR_ASCII
;
292 save_attr
= curr_attr
= ATTR_DEFAULT
;
293 term_editing
= term_echoing
= FALSE
;
294 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
295 app_cursor_keys
= cfg
.app_cursor
;
296 app_keypad_keys
= cfg
.app_keypad
;
298 blink_is_real
= cfg
.blinktext
;
299 erase_char
= ERASE_CHAR
;
303 for (i
= 0; i
< 256; i
++)
304 wordness
[i
] = cfg
.wordness
[i
];
308 erase_lots(FALSE
, TRUE
, TRUE
);
310 erase_lots(FALSE
, TRUE
, TRUE
);
315 * Force a screen update.
317 void term_update(void)
324 if ((seen_key_event
&& (cfg
.scroll_on_key
)) ||
325 (seen_disp_event
&& (cfg
.scroll_on_disp
))) {
326 disptop
= 0; /* return to main screen */
327 seen_disp_event
= seen_key_event
= 0;
330 sys_cursor(curs
.x
, curs
.y
- disptop
);
336 * Same as power_on(), but an external function.
338 void term_pwron(void)
348 * Clear the scrollback.
350 void term_clrsb(void)
354 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
361 * Initialise the terminal.
365 screen
= alt_screen
= scrollback
= NULL
;
367 disptext
= dispcurs
= NULL
;
372 beephead
= beeptail
= NULL
;
375 beep_overloaded
= FALSE
;
379 * Set up the terminal for a given size.
381 void term_size(int newrows
, int newcols
, int newsavelines
)
384 unsigned long *newdisp
, *line
;
387 int save_alt_which
= alt_which
;
389 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
390 return; /* nothing to do */
396 alt_b
= marg_b
= newrows
- 1;
399 scrollback
= newtree234(NULL
);
400 screen
= newtree234(NULL
);
405 * Resize the screen and scrollback. We only need to shift
406 * lines around within our data structures, because lineptr()
407 * will take care of resizing each individual line if
410 * - If the new screen and the old screen differ in length, we
411 * must shunt some lines in from the scrollback or out to
414 * - If doing that fails to provide us with enough material to
415 * fill the new screen (i.e. the number of rows needed in
416 * the new screen exceeds the total number in the previous
417 * screen+scrollback), we must invent some blank lines to
420 * - Then, if the new scrollback length is less than the
421 * amount of scrollback we actually have, we must throw some
424 sblen
= count234(scrollback
);
425 /* Do this loop to expand the screen if newrows > rows */
426 for (i
= rows
; i
< newrows
; i
++) {
428 line
= delpos234(scrollback
, --sblen
);
430 line
= smalloc(TSIZE
* (newcols
+ 2));
432 for (j
= 0; j
<= newcols
; j
++)
433 line
[j
+ 1] = ERASE_CHAR
;
435 addpos234(screen
, line
, 0);
437 /* Do this loop to shrink the screen if newrows < rows */
438 for (i
= newrows
; i
< rows
; i
++) {
439 line
= delpos234(screen
, 0);
440 addpos234(scrollback
, line
, sblen
++);
442 assert(count234(screen
) == newrows
);
443 while (sblen
> newsavelines
) {
444 line
= delpos234(scrollback
, 0);
448 assert(count234(scrollback
) <= newsavelines
);
451 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
452 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
453 newdisp
[i
] = ATTR_INVALID
;
458 newalt
= newtree234(NULL
);
459 for (i
= 0; i
< newrows
; i
++) {
460 line
= smalloc(TSIZE
* (newcols
+ 2));
462 for (j
= 0; j
<= newcols
; j
++)
463 line
[j
+ 1] = erase_char
;
464 addpos234(newalt
, line
, i
);
467 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
469 freetree234(alt_screen
);
473 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
476 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
477 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
481 curs
.y
+= newrows
- rows
;
484 if (curs
.y
>= newrows
)
485 curs
.y
= newrows
- 1;
486 if (curs
.x
>= newcols
)
487 curs
.x
= newcols
- 1;
489 wrapnext
= alt_wnext
= FALSE
;
493 savelines
= newsavelines
;
496 swap_screen(save_alt_which
);
505 static void swap_screen(int which
)
510 if (which
== alt_which
)
537 wrapnext
= alt_wnext
;
549 sco_acs
= alt_sco_acs
;
556 * Update the scroll bar.
558 static void update_sbar(void)
562 nscroll
= count234(scrollback
);
564 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
568 * Check whether the region bounded by the two pointers intersects
569 * the scroll region, and de-select the on-screen selection if so.
571 static void check_selection(pos from
, pos to
)
573 if (poslt(from
, selend
) && poslt(selstart
, to
))
578 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
579 * for backward.) `sb' is TRUE if the scrolling is permitted to
580 * affect the scrollback buffer.
582 * NB this function invalidates all pointers into lines of the
583 * screen data structures. In particular, you MUST call fix_cpos
584 * after calling scroll() and before doing anything else that
585 * uses the cpos shortcut pointer.
587 static void scroll(int topline
, int botline
, int lines
, int sb
)
589 unsigned long *line
, *line2
;
592 if (topline
!= 0 || alt_which
!= 0)
597 line
= delpos234(screen
, botline
);
598 line
= resizeline(line
, cols
);
599 for (i
= 0; i
< cols
; i
++)
600 line
[i
+ 1] = erase_char
;
602 addpos234(screen
, line
, topline
);
604 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
606 if (selstart
.y
> botline
) {
607 selstart
.y
= botline
;
611 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
613 if (selend
.y
> botline
) {
623 line
= delpos234(screen
, topline
);
624 if (sb
&& savelines
> 0) {
625 int sblen
= count234(scrollback
);
627 * We must add this line to the scrollback. We'll
628 * remove a line from the top of the scrollback to
629 * replace it, or allocate a new one if the
630 * scrollback isn't full.
632 if (sblen
== savelines
) {
633 sblen
--, line2
= delpos234(scrollback
, 0);
635 line2
= smalloc(TSIZE
* (cols
+ 2));
638 addpos234(scrollback
, line
, sblen
);
641 line
= resizeline(line
, cols
);
642 for (i
= 0; i
< cols
; i
++)
643 line
[i
+ 1] = erase_char
;
645 addpos234(screen
, line
, botline
);
647 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
649 if (selstart
.y
< topline
) {
650 selstart
.y
= topline
;
654 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
656 if (selend
.y
< topline
) {
668 * Move the cursor to a given position, clipping at boundaries. We
669 * may or may not want to clip at the scroll margin: marg_clip is 0
670 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
671 * even _being_ outside the margins.
673 static void move(int x
, int y
, int marg_clip
)
680 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
682 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
696 * Save or restore the cursor and SGR mode.
698 static void save_cursor(int save
)
702 save_attr
= curr_attr
;
705 save_csattr
= cset_attr
[cset
];
706 save_sco_acs
= sco_acs
;
709 /* Make sure the window hasn't shrunk since the save */
715 curr_attr
= save_attr
;
718 cset_attr
[cset
] = save_csattr
;
719 sco_acs
= save_sco_acs
;
722 erase_char
= (' ' | ATTR_ASCII
|
723 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
728 * Erase a large portion of the screen: the whole screen, or the
729 * whole line, or parts thereof.
731 static void erase_lots(int line_only
, int from_begin
, int to_end
)
735 unsigned long *ldata
;
757 check_selection(start
, end
);
759 /* Clear screen also forces a full window redraw, just in case. */
760 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
763 ldata
= lineptr(start
.y
);
764 while (poslt(start
, end
)) {
765 if (start
.x
== cols
&& !erase_lattr
)
766 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
768 ldata
[start
.x
] = erase_char
;
769 if (incpos(start
) && start
.y
< rows
)
770 ldata
= lineptr(start
.y
);
775 * Insert or delete characters within the current line. n is +ve if
776 * insertion is desired, and -ve for deletion.
778 static void insch(int n
)
780 int dir
= (n
< 0 ?
-1 : +1);
783 unsigned long *ldata
;
785 n
= (n
< 0 ?
-n
: n
);
786 if (n
> cols
- curs
.x
)
788 m
= cols
- curs
.x
- n
;
790 cursplus
.x
= curs
.x
+ n
;
791 check_selection(curs
, cursplus
);
792 ldata
= lineptr(curs
.y
);
794 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
796 ldata
[curs
.x
+ m
++] = erase_char
;
798 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
800 ldata
[curs
.x
+ n
] = erase_char
;
805 * Toggle terminal mode `mode' to state `state'. (`query' indicates
806 * whether the mode is a DEC private one or a normal one.)
808 static void toggle_mode(int mode
, int query
, int state
)
814 case 1: /* application cursor keys */
815 app_cursor_keys
= state
;
817 case 2: /* VT52 mode */
820 blink_is_real
= FALSE
;
823 blink_is_real
= cfg
.blinktext
;
826 case 3: /* 80/132 columns */
828 request_resize(state ?
132 : 80, rows
, 1);
831 case 5: /* reverse video */
833 * Toggle reverse video. If we receive an OFF within the
834 * visual bell timeout period after an ON, we trigger an
835 * effective visual bell, so that ESC[?5hESC[?5l will
836 * always be an actually _visible_ visual bell.
838 ticks
= GetTickCount();
839 if (rvideo
&& !state
&& /* we're turning it off */
840 ticks
< rvbell_timeout
) { /* and it's not long since it was turned on */
841 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
842 if (vbell_timeout
< rvbell_timeout
) /* don't move vbell end forward */
843 vbell_timeout
= rvbell_timeout
; /* vbell end is at least then */
844 } else if (!rvideo
&& state
) {
845 /* This is an ON, so we notice the time and save it. */
846 rvbell_timeout
= ticks
+ VBELL_TIMEOUT
;
849 seen_disp_event
= TRUE
;
853 case 6: /* DEC origin mode */
856 case 7: /* auto wrap */
859 case 8: /* auto key repeat */
862 case 10: /* set local edit mode */
863 term_editing
= state
;
864 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
866 case 25: /* enable/disable cursor */
867 compatibility2(OTHER
, VT220
);
869 seen_disp_event
= TRUE
;
871 case 47: /* alternate screen */
872 compatibility(OTHER
);
877 case 1000: /* xterm mouse 1 */
878 xterm_mouse
= state ?
1 : 0;
879 set_raw_mouse_mode(state
);
881 case 1002: /* xterm mouse 2 */
882 xterm_mouse
= state ?
2 : 0;
883 set_raw_mouse_mode(state
);
887 case 4: /* set insert mode */
888 compatibility(VT102
);
891 case 12: /* set echo mode */
892 term_echoing
= !state
;
893 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
895 case 20: /* Return sends ... */
896 cr_lf_return
= state
;
898 case 34: /* Make cursor BIG */
899 compatibility2(OTHER
, VT220
);
905 * Process an OSC sequence: set window title or icon name.
907 static void do_osc(void)
911 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
913 osc_string
[osc_strlen
] = '\0';
914 switch (esc_args
[0]) {
917 set_icon(osc_string
);
918 if (esc_args
[0] == 1)
920 /* fall through: parameter 0 means set both */
923 set_title(osc_string
);
930 * Remove everything currently in `inbuf' and stick it up on the
931 * in-memory display. There's a big state machine in here to
932 * process escape sequences...
939 * Optionally log the session traffic to a file. Useful for
940 * debugging and possibly also useful for actual logging.
942 if (cfg
.logtype
== LGTYP_DEBUG
)
943 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
944 logtraffic((unsigned char) inbuf
[inbuf_reap
], LGTYP_DEBUG
);
947 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
948 c
= inbuf
[inbuf_reap
];
950 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
951 * be able to display 8-bit characters, but I'll let that go 'cause
955 /* First see about all those translations. */
956 if (termstate
== TOPLEVEL
) {
961 /* UTF-8 must be stateless so we ignore iso2022. */
962 if (unitab_ctrl
[c
] != 0xFF)
964 else c
= ((unsigned char)c
) | ATTR_ASCII
;
966 } else if ((c
& 0xe0) == 0xc0) {
967 utf_size
= utf_state
= 1;
968 utf_char
= (c
& 0x1f);
969 } else if ((c
& 0xf0) == 0xe0) {
970 utf_size
= utf_state
= 2;
971 utf_char
= (c
& 0x0f);
972 } else if ((c
& 0xf8) == 0xf0) {
973 utf_size
= utf_state
= 3;
974 utf_char
= (c
& 0x07);
975 } else if ((c
& 0xfc) == 0xf8) {
976 utf_size
= utf_state
= 4;
977 utf_char
= (c
& 0x03);
978 } else if ((c
& 0xfe) == 0xfc) {
979 utf_size
= utf_state
= 5;
980 utf_char
= (c
& 0x01);
991 if ((c
& 0xC0) != 0x80) {
997 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
1003 /* Is somebody trying to be evil! */
1005 (c
< 0x800 && utf_size
>= 2) ||
1006 (c
< 0x10000 && utf_size
>= 3) ||
1007 (c
< 0x200000 && utf_size
>= 4) ||
1008 (c
< 0x4000000 && utf_size
>= 5))
1011 /* Unicode line separator and paragraph separator are CR-LF */
1012 if (c
== 0x2028 || c
== 0x2029)
1015 /* High controls are probably a Baaad idea too. */
1019 /* The UTF-16 surrogates are not nice either. */
1020 /* The standard give the option of decoding these:
1021 * I don't want to! */
1022 if (c
>= 0xD800 && c
< 0xE000)
1025 /* ISO 10646 characters now limited to UTF-16 range. */
1029 /* This is currently a TagPhobic application.. */
1030 if (c
>= 0xE0000 && c
<= 0xE007F)
1033 /* U+FEFF is best seen as a null. */
1036 /* But U+FFFE is an error. */
1037 if (c
== 0xFFFE || c
== 0xFFFF)
1040 /* Oops this is a 16bit implementation */
1045 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1047 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1049 if (sco_acs
== 2) c
^= 0x80;
1052 switch (cset_attr
[cset
]) {
1054 * Linedraw characters are different from 'ESC ( B'
1055 * only for a small range. For ones outside that
1056 * range, make sure we use the same font as well as
1057 * the same encoding.
1060 if (unitab_ctrl
[c
] != 0xFF)
1063 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1067 /* If UK-ASCII, make the '#' a LineDraw Pound */
1069 c
= '}' | ATTR_LINEDRW
;
1072 /*FALLTHROUGH*/ case ATTR_ASCII
:
1073 if (unitab_ctrl
[c
] != 0xFF)
1076 c
= ((unsigned char) c
) | ATTR_ASCII
;
1079 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1085 /* How about C1 controls ? */
1086 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1087 has_compat(VT220
)) {
1088 termstate
= SEEN_ESC
;
1090 c
= '@' + (c
& 0x1F);
1093 /* Or the GL control. */
1094 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1095 if (curs
.x
&& !wrapnext
)
1099 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1101 /* Or normal C0 controls. */
1102 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1104 case '\005': /* terminal type query */
1105 /* Strictly speaking this is VT100 but a VT100 defaults to
1106 * no response. Other terminals respond at their option.
1108 * Don't put a CR in the default string as this tends to
1109 * upset some weird software.
1111 * An xterm returns "xterm" (5 characters)
1113 compatibility(ANSIMIN
);
1115 char abuf
[256], *s
, *d
;
1117 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1119 if (*s
>= 'a' && *s
<= 'z')
1120 *d
++ = (*s
- ('a' - 1));
1121 else if ((*s
>= '@' && *s
<= '_') ||
1122 *s
== '?' || (*s
& 0x80))
1127 } else if (*s
== '^') {
1132 lpage_send(CP_ACP
, abuf
, d
- abuf
);
1137 struct beeptime
*newbeep
;
1140 ticks
= GetTickCount();
1142 if (!beep_overloaded
) {
1143 newbeep
= smalloc(sizeof(struct beeptime
));
1144 newbeep
->ticks
= ticks
;
1145 newbeep
->next
= NULL
;
1149 beeptail
->next
= newbeep
;
1155 * Throw out any beeps that happened more than
1159 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1160 struct beeptime
*tmp
= beephead
;
1161 beephead
= tmp
->next
;
1168 if (cfg
.bellovl
&& beep_overloaded
&&
1169 ticks
- lastbeep
>= cfg
.bellovl_s
) {
1171 * If we're currently overloaded and the
1172 * last beep was more than s seconds ago,
1173 * leave overload mode.
1175 beep_overloaded
= FALSE
;
1176 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1177 nbeeps
>= cfg
.bellovl_n
) {
1179 * Now, if we have n or more beeps
1180 * remaining in the queue, go into overload
1183 beep_overloaded
= TRUE
;
1188 * Perform an actual beep if we're not overloaded.
1190 if (!cfg
.bellovl
|| !beep_overloaded
) {
1192 if (cfg
.beep
== BELL_VISUAL
) {
1194 vbell_timeout
= ticks
+ VBELL_TIMEOUT
;
1202 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1203 else if (curs
.x
== 0 && curs
.y
> 0)
1204 curs
.x
= cols
- 1, curs
.y
--;
1210 seen_disp_event
= TRUE
;
1213 compatibility(VT100
);
1217 compatibility(VT100
);
1222 termstate
= VT52_ESC
;
1224 compatibility(ANSIMIN
);
1225 termstate
= SEEN_ESC
;
1233 seen_disp_event
= TRUE
;
1235 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1238 if (has_compat(SCOANSI
)) {
1240 erase_lots(FALSE
, FALSE
, TRUE
);
1243 seen_disp_event
= 1;
1247 compatibility(VT100
);
1249 if (curs
.y
== marg_b
)
1250 scroll(marg_t
, marg_b
, 1, TRUE
);
1251 else if (curs
.y
< rows
- 1)
1257 seen_disp_event
= 1;
1259 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1263 pos old_curs
= curs
;
1264 unsigned long *ldata
= lineptr(curs
.y
);
1268 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1270 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1271 if (curs
.x
>= cols
/ 2)
1272 curs
.x
= cols
/ 2 - 1;
1279 check_selection(old_curs
, curs
);
1281 seen_disp_event
= TRUE
;
1285 switch (termstate
) {
1287 /* Only graphic characters get this far, ctrls are stripped above */
1288 if (wrapnext
&& wrap
) {
1289 cpos
[1] |= LATTR_WRAPPED
;
1290 if (curs
.y
== marg_b
)
1291 scroll(marg_t
, marg_b
, 1, TRUE
);
1292 else if (curs
.y
< rows
- 1)
1300 if (selstate
!= NO_SELECTION
) {
1301 pos cursplus
= curs
;
1303 check_selection(curs
, cursplus
);
1305 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1306 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1308 extern int wcwidth(wchar_t ucs
);
1313 width
= wcwidth((wchar_t) c
);
1316 if (curs
.x
+ 1 != cols
) {
1317 *cpos
++ = c
| ATTR_WIDE
| curr_attr
;
1318 *cpos
++ = UCSWIDE
| curr_attr
;
1323 *cpos
++ = c
| curr_attr
;
1330 if (curs
.x
== cols
) {
1334 if (wrap
&& vt52_mode
) {
1335 cpos
[1] |= LATTR_WRAPPED
;
1336 if (curs
.y
== marg_b
)
1337 scroll(marg_t
, marg_b
, 1, TRUE
);
1338 else if (curs
.y
< rows
- 1)
1345 seen_disp_event
= 1;
1350 * This state is virtually identical to SEEN_ESC, with the
1351 * exception that we have an OSC sequence in the pipeline,
1352 * and _if_ we see a backslash, we process it.
1356 termstate
= TOPLEVEL
;
1359 /* else fall through */
1361 if (c
>= ' ' && c
<= '/') {
1368 termstate
= TOPLEVEL
;
1369 switch (ANSI(c
, esc_query
)) {
1370 case '[': /* enter CSI mode */
1371 termstate
= SEEN_CSI
;
1373 esc_args
[0] = ARG_DEFAULT
;
1376 case ']': /* xterm escape sequences */
1377 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1378 compatibility(OTHER
);
1379 termstate
= SEEN_OSC
;
1382 case '7': /* save cursor */
1383 compatibility(VT100
);
1386 case '8': /* restore cursor */
1387 compatibility(VT100
);
1389 seen_disp_event
= TRUE
;
1392 compatibility(VT100
);
1393 app_keypad_keys
= TRUE
;
1396 compatibility(VT100
);
1397 app_keypad_keys
= FALSE
;
1399 case 'D': /* exactly equivalent to LF */
1400 compatibility(VT100
);
1401 if (curs
.y
== marg_b
)
1402 scroll(marg_t
, marg_b
, 1, TRUE
);
1403 else if (curs
.y
< rows
- 1)
1407 seen_disp_event
= TRUE
;
1409 case 'E': /* exactly equivalent to CR-LF */
1410 compatibility(VT100
);
1412 if (curs
.y
== marg_b
)
1413 scroll(marg_t
, marg_b
, 1, TRUE
);
1414 else if (curs
.y
< rows
- 1)
1418 seen_disp_event
= TRUE
;
1420 case 'M': /* reverse index - backwards LF */
1421 compatibility(VT100
);
1422 if (curs
.y
== marg_t
)
1423 scroll(marg_t
, marg_b
, -1, TRUE
);
1424 else if (curs
.y
> 0)
1428 seen_disp_event
= TRUE
;
1430 case 'Z': /* terminal type query */
1431 compatibility(VT100
);
1432 ldisc_send(id_string
, strlen(id_string
));
1434 case 'c': /* restore power-on settings */
1435 compatibility(VT100
);
1438 request_resize(80, rows
, 1);
1443 seen_disp_event
= TRUE
;
1445 case 'H': /* set a tab */
1446 compatibility(VT100
);
1447 tabs
[curs
.x
] = TRUE
;
1450 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1451 compatibility(VT100
);
1453 unsigned long *ldata
;
1457 for (i
= 0; i
< rows
; i
++) {
1459 for (j
= 0; j
< cols
; j
++)
1460 ldata
[j
] = ATTR_DEFAULT
| 'E';
1464 seen_disp_event
= TRUE
;
1465 scrtop
.x
= scrtop
.y
= 0;
1468 check_selection(scrtop
, scrbot
);
1472 case ANSI('3', '#'):
1473 case ANSI('4', '#'):
1474 case ANSI('5', '#'):
1475 case ANSI('6', '#'):
1476 compatibility(VT100
);
1478 unsigned long nlattr
;
1479 unsigned long *ldata
;
1480 switch (ANSI(c
, esc_query
)) {
1481 case ANSI('3', '#'):
1484 case ANSI('4', '#'):
1487 case ANSI('5', '#'):
1488 nlattr
= LATTR_NORM
;
1490 default: /* spiritually case ANSI('6', '#'): */
1491 nlattr
= LATTR_WIDE
;
1494 ldata
= lineptr(curs
.y
);
1495 ldata
[cols
] &= ~LATTR_MODE
;
1496 ldata
[cols
] |= nlattr
;
1500 case ANSI('A', '('):
1501 compatibility(VT100
);
1502 cset_attr
[0] = ATTR_GBCHR
;
1504 case ANSI('B', '('):
1505 compatibility(VT100
);
1506 cset_attr
[0] = ATTR_ASCII
;
1508 case ANSI('0', '('):
1509 compatibility(VT100
);
1510 cset_attr
[0] = ATTR_LINEDRW
;
1512 case ANSI('U', '('):
1513 compatibility(OTHER
);
1514 cset_attr
[0] = ATTR_SCOACS
;
1517 case ANSI('A', ')'):
1518 compatibility(VT100
);
1519 cset_attr
[1] = ATTR_GBCHR
;
1521 case ANSI('B', ')'):
1522 compatibility(VT100
);
1523 cset_attr
[1] = ATTR_ASCII
;
1525 case ANSI('0', ')'):
1526 compatibility(VT100
);
1527 cset_attr
[1] = ATTR_LINEDRW
;
1529 case ANSI('U', ')'):
1530 compatibility(OTHER
);
1531 cset_attr
[1] = ATTR_SCOACS
;
1534 case ANSI('8', '%'): /* Old Linux code */
1535 case ANSI('G', '%'):
1536 compatibility(OTHER
);
1539 case ANSI('@', '%'):
1540 compatibility(OTHER
);
1546 termstate
= TOPLEVEL
; /* default */
1548 if (esc_nargs
<= ARGS_MAX
) {
1549 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1550 esc_args
[esc_nargs
- 1] = 0;
1551 esc_args
[esc_nargs
- 1] =
1552 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1554 termstate
= SEEN_CSI
;
1555 } else if (c
== ';') {
1556 if (++esc_nargs
<= ARGS_MAX
)
1557 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1558 termstate
= SEEN_CSI
;
1559 } else if (c
< '@') {
1566 termstate
= SEEN_CSI
;
1568 switch (ANSI(c
, esc_query
)) {
1569 case 'A': /* move up N lines */
1570 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1571 seen_disp_event
= TRUE
;
1573 case 'e': /* move down N lines */
1574 compatibility(ANSI
);
1577 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1578 seen_disp_event
= TRUE
;
1580 case ANSI('c', '>'): /* report xterm version */
1581 compatibility(OTHER
);
1582 /* this reports xterm version 136 so that VIM can
1583 use the drag messages from the mouse reporting */
1584 ldisc_send("\033[>0;136;0c", 11);
1586 case 'a': /* move right N cols */
1587 compatibility(ANSI
);
1590 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1591 seen_disp_event
= TRUE
;
1593 case 'D': /* move left N cols */
1594 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1595 seen_disp_event
= TRUE
;
1597 case 'E': /* move down N lines and CR */
1598 compatibility(ANSI
);
1599 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1600 seen_disp_event
= TRUE
;
1602 case 'F': /* move up N lines and CR */
1603 compatibility(ANSI
);
1604 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1605 seen_disp_event
= TRUE
;
1608 case '`': /* set horizontal posn */
1609 compatibility(ANSI
);
1610 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1611 seen_disp_event
= TRUE
;
1613 case 'd': /* set vertical posn */
1614 compatibility(ANSI
);
1616 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1619 seen_disp_event
= TRUE
;
1622 case 'f': /* set horz and vert posns at once */
1624 esc_args
[1] = ARG_DEFAULT
;
1625 move(def(esc_args
[1], 1) - 1,
1626 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1629 seen_disp_event
= TRUE
;
1631 case 'J': /* erase screen or parts of it */
1633 unsigned int i
= def(esc_args
[0], 0) + 1;
1636 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1639 seen_disp_event
= TRUE
;
1641 case 'K': /* erase line or parts of it */
1643 unsigned int i
= def(esc_args
[0], 0) + 1;
1646 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1648 seen_disp_event
= TRUE
;
1650 case 'L': /* insert lines */
1651 compatibility(VT102
);
1652 if (curs
.y
<= marg_b
)
1653 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1656 seen_disp_event
= TRUE
;
1658 case 'M': /* delete lines */
1659 compatibility(VT102
);
1660 if (curs
.y
<= marg_b
)
1661 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1664 seen_disp_event
= TRUE
;
1666 case '@': /* insert chars */
1667 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1668 compatibility(VT102
);
1669 insch(def(esc_args
[0], 1));
1670 seen_disp_event
= TRUE
;
1672 case 'P': /* delete chars */
1673 compatibility(VT102
);
1674 insch(-def(esc_args
[0], 1));
1675 seen_disp_event
= TRUE
;
1677 case 'c': /* terminal type query */
1678 compatibility(VT100
);
1679 /* This is the response for a VT102 */
1680 ldisc_send(id_string
, strlen(id_string
));
1682 case 'n': /* cursor position query */
1683 if (esc_args
[0] == 6) {
1685 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1687 ldisc_send(buf
, strlen(buf
));
1688 } else if (esc_args
[0] == 5) {
1689 ldisc_send("\033[0n", 4);
1692 case 'h': /* toggle modes to high */
1694 compatibility(VT100
);
1697 for (i
= 0; i
< esc_nargs
; i
++)
1698 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1701 case 'l': /* toggle modes to low */
1703 compatibility(VT100
);
1706 for (i
= 0; i
< esc_nargs
; i
++)
1707 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1710 case 'g': /* clear tabs */
1711 compatibility(VT100
);
1712 if (esc_nargs
== 1) {
1713 if (esc_args
[0] == 0) {
1714 tabs
[curs
.x
] = FALSE
;
1715 } else if (esc_args
[0] == 3) {
1717 for (i
= 0; i
< cols
; i
++)
1722 case 'r': /* set scroll margins */
1723 compatibility(VT100
);
1724 if (esc_nargs
<= 2) {
1726 top
= def(esc_args
[0], 1) - 1;
1727 bot
= (esc_nargs
<= 1
1729 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1732 /* VTTEST Bug 9 - if region is less than 2 lines
1733 * don't change region.
1735 if (bot
- top
> 0) {
1740 * I used to think the cursor should be
1741 * placed at the top of the newly marginned
1742 * area. Apparently not: VMS TPU falls over
1745 * Well actually it should for Origin mode - RDB
1747 curs
.y
= (dec_om ? marg_t
: 0);
1749 seen_disp_event
= TRUE
;
1753 case 'm': /* set graphics rendition */
1756 * A VT100 without the AVO only had one attribute, either
1757 * underline or reverse video depending on the cursor type,
1758 * this was selected by CSI 7m.
1761 * This is sometimes DIM, eg on the GIGI and Linux
1763 * This is sometimes INVIS various ANSI.
1765 * This like 22 disables BOLD, DIM and INVIS
1767 * The ANSI colours appear on any terminal that has colour
1768 * (obviously) but the interaction between sgr0 and the
1769 * colours varies but is usually related to the background
1770 * colour erase item.
1771 * The interaction between colour attributes and the mono
1772 * ones is also very implementation dependent.
1774 * The 39 and 49 attributes are likely to be unimplemented.
1777 for (i
= 0; i
< esc_nargs
; i
++) {
1778 switch (def(esc_args
[i
], 0)) {
1779 case 0: /* restore defaults */
1780 curr_attr
= ATTR_DEFAULT
;
1782 case 1: /* enable bold */
1783 compatibility(VT100AVO
);
1784 curr_attr
|= ATTR_BOLD
;
1786 case 21: /* (enable double underline) */
1787 compatibility(OTHER
);
1788 case 4: /* enable underline */
1789 compatibility(VT100AVO
);
1790 curr_attr
|= ATTR_UNDER
;
1792 case 5: /* enable blink */
1793 compatibility(VT100AVO
);
1794 curr_attr
|= ATTR_BLINK
;
1796 case 7: /* enable reverse video */
1797 curr_attr
|= ATTR_REVERSE
;
1799 case 10: /* SCO acs off */
1800 compatibility(SCOANSI
);
1802 case 11: /* SCO acs on */
1803 compatibility(SCOANSI
);
1805 case 12: /* SCO acs on flipped */
1806 compatibility(SCOANSI
);
1808 case 22: /* disable bold */
1809 compatibility2(OTHER
, VT220
);
1810 curr_attr
&= ~ATTR_BOLD
;
1812 case 24: /* disable underline */
1813 compatibility2(OTHER
, VT220
);
1814 curr_attr
&= ~ATTR_UNDER
;
1816 case 25: /* disable blink */
1817 compatibility2(OTHER
, VT220
);
1818 curr_attr
&= ~ATTR_BLINK
;
1820 case 27: /* disable reverse video */
1821 compatibility2(OTHER
, VT220
);
1822 curr_attr
&= ~ATTR_REVERSE
;
1833 curr_attr
&= ~ATTR_FGMASK
;
1835 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1837 case 39: /* default-foreground */
1838 curr_attr
&= ~ATTR_FGMASK
;
1839 curr_attr
|= ATTR_DEFFG
;
1850 curr_attr
&= ~ATTR_BGMASK
;
1852 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1854 case 49: /* default-background */
1855 curr_attr
&= ~ATTR_BGMASK
;
1856 curr_attr
|= ATTR_DEFBG
;
1861 erase_char
= (' ' | ATTR_ASCII
|
1863 (ATTR_FGMASK
| ATTR_BGMASK
)));
1866 case 's': /* save cursor */
1869 case 'u': /* restore cursor */
1871 seen_disp_event
= TRUE
;
1873 case 't': /* set page size - ie window height */
1875 * VT340/VT420 sequence DECSLPP, DEC only allows values
1876 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1877 * illegal values (eg first arg 1..9) for window changing
1880 compatibility(VT340TEXT
);
1882 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1883 request_resize(cols
, def(esc_args
[0], 24), 0);
1888 compatibility(SCOANSI
);
1889 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
1892 seen_disp_event
= TRUE
;
1895 compatibility(SCOANSI
);
1896 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
1899 seen_disp_event
= TRUE
;
1901 case ANSI('|', '*'):
1902 /* VT420 sequence DECSNLS
1903 * Set number of lines on screen
1904 * VT420 uses VGA like hardware and can support any size in
1905 * reasonable range (24..49 AIUI) with no default specified.
1907 compatibility(VT420
);
1908 if (esc_nargs
== 1 && esc_args
[0] > 0) {
1909 request_resize(cols
,
1910 def(esc_args
[0], cfg
.height
),
1915 case ANSI('|', '$'):
1916 /* VT340/VT420 sequence DECSCPP
1917 * Set number of columns per page
1918 * Docs imply range is only 80 or 132, but I'll allow any.
1920 compatibility(VT340TEXT
);
1921 if (esc_nargs
<= 1) {
1922 request_resize(def(esc_args
[0], cfg
.width
),
1927 case 'X': /* write N spaces w/o moving cursor */
1928 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1929 compatibility(ANSIMIN
);
1931 int n
= def(esc_args
[0], 1);
1933 unsigned long *p
= cpos
;
1934 if (n
> cols
- curs
.x
)
1938 check_selection(curs
, cursplus
);
1941 seen_disp_event
= TRUE
;
1944 case 'x': /* report terminal characteristics */
1945 compatibility(VT100
);
1948 int i
= def(esc_args
[0], 0);
1949 if (i
== 0 || i
== 1) {
1950 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
1952 ldisc_send(buf
, 20);
1956 case 'Z': /* BackTab for xterm */
1957 compatibility(OTHER
);
1959 int i
= def(esc_args
[0], 1);
1960 pos old_curs
= curs
;
1962 for(;i
>0 && curs
.x
>0; i
--) {
1965 } while (curs
.x
>0 && !tabs
[curs
.x
]);
1968 check_selection(old_curs
, curs
);
1971 case ANSI('L', '='):
1972 compatibility(OTHER
);
1973 use_bce
= (esc_args
[0] <= 0);
1974 erase_char
= ERASE_CHAR
;
1976 erase_char
= (' ' | ATTR_ASCII
|
1978 (ATTR_FGMASK
| ATTR_BGMASK
)));
1980 case ANSI('E', '='):
1981 compatibility(OTHER
);
1982 blink_is_real
= (esc_args
[0] >= 1);
1984 case ANSI('p', '"'):
1985 /* Allow the host to make this emulator a 'perfect' VT102.
1986 * This first appeared in the VT220, but we do need to get
1987 * back to PuTTY mode so I won't check it.
1989 * The arg in 40..42,50 are a PuTTY extension.
1990 * The 2nd arg, 8bit vs 7bit is not checked.
1992 * Setting VT102 mode should also change the Fkeys to
1993 * generate PF* codes as a real VT102 has no Fkeys.
1994 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1997 * Note ESC c will NOT change this!
2000 switch (esc_args
[0]) {
2002 compatibility_level
&= ~TM_VTXXX
;
2003 compatibility_level
|= TM_VT102
;
2006 compatibility_level
&= ~TM_VTXXX
;
2007 compatibility_level
|= TM_VT220
;
2011 if (esc_args
[0] > 60 && esc_args
[0] < 70)
2012 compatibility_level
|= TM_VTXXX
;
2016 compatibility_level
&= TM_VTXXX
;
2019 compatibility_level
= TM_PUTTY
;
2022 compatibility_level
= TM_SCOANSI
;
2026 compatibility_level
= TM_PUTTY
;
2032 /* Change the response to CSI c */
2033 if (esc_args
[0] == 50) {
2036 strcpy(id_string
, "\033[?");
2037 for (i
= 1; i
< esc_nargs
; i
++) {
2039 strcat(id_string
, ";");
2040 sprintf(lbuf
, "%d", esc_args
[i
]);
2041 strcat(id_string
, lbuf
);
2043 strcat(id_string
, "c");
2046 /* Is this a good idea ?
2047 * Well we should do a soft reset at this point ...
2049 if (!has_compat(VT420
) && has_compat(VT100
)) {
2051 request_resize(132, 24, 1);
2053 request_resize(80, 24, 1);
2062 case 'P': /* Linux palette sequence */
2063 termstate
= SEEN_OSC_P
;
2066 case 'R': /* Linux palette reset */
2069 termstate
= TOPLEVEL
;
2071 case 'W': /* word-set */
2072 termstate
= SEEN_OSC_W
;
2085 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2089 * Grotty hack to support xterm and DECterm title
2090 * sequences concurrently.
2092 if (esc_args
[0] == 2) {
2096 /* else fall through */
2098 termstate
= OSC_STRING
;
2104 * This OSC stuff is EVIL. It takes just one character to get into
2105 * sysline mode and it's not initially obvious how to get out.
2106 * So I've added CR and LF as string aborts.
2107 * This shouldn't effect compatibility as I believe embedded
2108 * control characters are supposed to be interpreted (maybe?)
2109 * and they don't display anything useful anyway.
2113 if (c
== '\n' || c
== '\r') {
2114 termstate
= TOPLEVEL
;
2115 } else if (c
== 0234 || c
== '\007') {
2117 * These characters terminate the string; ST and BEL
2118 * terminate the sequence and trigger instant
2119 * processing of it, whereas ESC goes back to SEEN_ESC
2120 * mode unless it is followed by \, in which case it is
2121 * synonymous with ST in the first place.
2124 termstate
= TOPLEVEL
;
2125 } else if (c
== '\033')
2126 termstate
= OSC_MAYBE_ST
;
2127 else if (osc_strlen
< OSC_STR_MAX
)
2128 osc_string
[osc_strlen
++] = c
;
2132 int max
= (osc_strlen
== 0 ?
21 : 16);
2134 if (c
>= '0' && c
<= '9')
2136 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2138 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2141 termstate
= TOPLEVEL
;
2144 osc_string
[osc_strlen
++] = val
;
2145 if (osc_strlen
>= 7) {
2146 palette_set(osc_string
[0],
2147 osc_string
[1] * 16 + osc_string
[2],
2148 osc_string
[3] * 16 + osc_string
[4],
2149 osc_string
[5] * 16 + osc_string
[6]);
2151 termstate
= TOPLEVEL
;
2167 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2170 termstate
= OSC_STRING
;
2175 termstate
= TOPLEVEL
;
2176 seen_disp_event
= TRUE
;
2179 move(curs
.x
, curs
.y
- 1, 1);
2182 move(curs
.x
, curs
.y
+ 1, 1);
2185 move(curs
.x
+ 1, curs
.y
, 1);
2188 move(curs
.x
- 1, curs
.y
, 1);
2191 * From the VT100 Manual
2192 * NOTE: The special graphics characters in the VT100
2193 * are different from those in the VT52
2195 * From VT102 manual:
2196 * 137 _ Blank - Same
2197 * 140 ` Reserved - Humm.
2198 * 141 a Solid rectangle - Similar
2199 * 142 b 1/ - Top half of fraction for the
2200 * 143 c 3/ - subscript numbers below.
2203 * 146 f Degrees - Same
2204 * 147 g Plus or minus - Same
2206 * 151 i Ellipsis (dots)
2209 * 154 l Bar at scan 0
2210 * 155 m Bar at scan 1
2211 * 156 n Bar at scan 2
2212 * 157 o Bar at scan 3 - Similar
2213 * 160 p Bar at scan 4 - Similar
2214 * 161 q Bar at scan 5 - Similar
2215 * 162 r Bar at scan 6 - Same
2216 * 163 s Bar at scan 7 - Similar
2231 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2234 cset_attr
[cset
= 0] = ATTR_ASCII
;
2241 scroll(0, rows
- 1, -1, TRUE
);
2242 else if (curs
.y
> 0)
2248 erase_lots(FALSE
, FALSE
, TRUE
);
2252 erase_lots(TRUE
, FALSE
, TRUE
);
2256 /* XXX Print cursor line */
2259 /* XXX Start controller mode */
2262 /* XXX Stop controller mode */
2266 termstate
= VT52_Y1
;
2269 ldisc_send("\033/Z", 3);
2272 app_keypad_keys
= TRUE
;
2275 app_keypad_keys
= FALSE
;
2278 /* XXX This should switch to VT100 mode not current or default
2279 * VT mode. But this will only have effect in a VT220+
2283 blink_is_real
= cfg
.blinktext
;
2287 /* XXX Enter auto print mode */
2290 /* XXX Exit auto print mode */
2293 /* XXX Print screen */
2299 /* compatibility(ATARI) */
2301 erase_lots(FALSE
, FALSE
, TRUE
);
2305 /* compatibility(ATARI) */
2306 if (curs
.y
<= marg_b
)
2307 scroll(curs
.y
, marg_b
, -1, FALSE
);
2310 /* compatibility(ATARI) */
2311 if (curs
.y
<= marg_b
)
2312 scroll(curs
.y
, marg_b
, 1, TRUE
);
2315 /* compatibility(ATARI) */
2316 termstate
= VT52_FG
;
2319 /* compatibility(ATARI) */
2320 termstate
= VT52_BG
;
2323 /* compatibility(ATARI) */
2324 erase_lots(FALSE
, TRUE
, FALSE
);
2328 /* compatibility(ATARI) */
2332 /* compatibility(ATARI) */
2335 /* case 'j': Save cursor position - broken on ST */
2336 /* case 'k': Restore cursor position */
2338 /* compatibility(ATARI) */
2339 erase_lots(TRUE
, TRUE
, TRUE
);
2345 /* compatibility(ATARI) */
2346 erase_lots(TRUE
, TRUE
, FALSE
);
2349 /* compatibility(ATARI) */
2350 curr_attr
|= ATTR_REVERSE
;
2353 /* compatibility(ATARI) */
2354 curr_attr
&= ~ATTR_REVERSE
;
2356 case 'v': /* wrap Autowrap on - Wyse style */
2357 /* compatibility(ATARI) */
2360 case 'w': /* Autowrap off */
2361 /* compatibility(ATARI) */
2366 /* compatibility(OTHER) */
2368 curr_attr
= ATTR_DEFAULT
;
2370 erase_char
= (' ' | ATTR_ASCII
|
2372 (ATTR_FGMASK
| ATTR_BGMASK
)));
2375 /* compatibility(VI50) */
2376 curr_attr
|= ATTR_UNDER
;
2379 /* compatibility(VI50) */
2380 curr_attr
&= ~ATTR_UNDER
;
2383 /* compatibility(VI50) */
2385 curr_attr
|= ATTR_BOLD
;
2388 /* compatibility(VI50) */
2390 curr_attr
&= ~ATTR_BOLD
;
2396 termstate
= VT52_Y2
;
2397 move(curs
.x
, c
- ' ', 0);
2400 termstate
= TOPLEVEL
;
2401 move(c
- ' ', curs
.y
, 0);
2406 termstate
= TOPLEVEL
;
2407 curr_attr
&= ~ATTR_FGMASK
;
2408 curr_attr
&= ~ATTR_BOLD
;
2409 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2410 if ((c
& 0x8) || vt52_bold
)
2411 curr_attr
|= ATTR_BOLD
;
2414 erase_char
= (' ' | ATTR_ASCII
|
2415 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2418 termstate
= TOPLEVEL
;
2419 curr_attr
&= ~ATTR_BGMASK
;
2420 curr_attr
&= ~ATTR_BLINK
;
2421 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2423 /* Note: bold background */
2425 curr_attr
|= ATTR_BLINK
;
2428 erase_char
= (' ' | ATTR_ASCII
|
2429 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2432 default: break; /* placate gcc warning about enum use */
2434 if (selstate
!= NO_SELECTION
) {
2435 pos cursplus
= curs
;
2437 check_selection(curs
, cursplus
);
2445 * Compare two lines to determine whether they are sufficiently
2446 * alike to scroll-optimise one to the other. Return the degree of
2449 static int linecmp(unsigned long *a
, unsigned long *b
)
2453 for (i
= n
= 0; i
< cols
; i
++)
2454 n
+= (*a
++ == *b
++);
2460 * Given a context, update the window. Out of paranoia, we don't
2461 * allow WM_PAINT responses to do scrolling optimisations.
2463 static void do_paint(Context ctx
, int may_optimise
)
2465 int i
, j
, our_curs_y
;
2466 unsigned long rv
, cursor
;
2469 long cursor_background
= ERASE_CHAR
;
2473 * Check the visual bell state.
2476 ticks
= GetTickCount();
2477 if (ticks
- vbell_timeout
>= 0)
2481 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2484 * screen array, disptop, scrtop,
2486 * cfg.blinkpc, blink_is_real, tblinker,
2487 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2490 /* Has the cursor position or type changed ? */
2493 if (blinker
|| !cfg
.blink_cur
)
2494 cursor
= TATTR_ACTCURS
;
2498 cursor
= TATTR_PASCURS
;
2500 cursor
|= TATTR_RIGHTCURS
;
2503 our_curs_y
= curs
.y
- disptop
;
2505 if (dispcurs
&& (curstype
!= cursor
||
2507 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2508 if (dispcurs
> disptext
&& (dispcurs
[-1] & ATTR_WIDE
))
2509 dispcurs
[-1] |= ATTR_INVALID
;
2510 if ((*dispcurs
& ATTR_WIDE
))
2511 dispcurs
[1] |= ATTR_INVALID
;
2512 *dispcurs
|= ATTR_INVALID
;
2517 /* The normal screen data */
2518 for (i
= 0; i
< rows
; i
++) {
2519 unsigned long *ldata
;
2521 int idx
, dirty_line
, dirty_run
;
2522 unsigned long attr
= 0;
2523 int updated_line
= 0;
2526 int last_run_dirty
= 0;
2528 scrpos
.y
= i
+ disptop
;
2529 ldata
= lineptr(scrpos
.y
);
2530 lattr
= (ldata
[cols
] & LATTR_MODE
);
2532 idx
= i
* (cols
+ 1);
2533 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2534 disptext
[idx
+ cols
] = ldata
[cols
];
2536 for (j
= 0; j
< cols
; j
++, idx
++) {
2537 unsigned long tattr
, tchar
;
2538 unsigned long *d
= ldata
+ j
;
2542 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2543 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2544 switch (tchar
& CSET_MASK
) {
2546 tchar
= unitab_line
[tchar
& 0xFF];
2549 tchar
= unitab_xterm
[tchar
& 0xFF];
2552 tchar
= unitab_scoacs
[tchar
&0xFF];
2555 tattr
|= (tchar
& CSET_MASK
);
2558 /* Video reversing things */
2560 ^ (posle(selstart
, scrpos
) &&
2561 poslt(scrpos
, selend
) ? ATTR_REVERSE
: 0));
2563 /* 'Real' blinking ? */
2564 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2565 if (has_focus
&& tblinker
) {
2567 tattr
&= ~CSET_MASK
;
2570 tattr
&= ~ATTR_BLINK
;
2573 /* Cursor here ? Save the 'background' */
2574 if (i
== our_curs_y
&& j
== curs
.x
) {
2575 cursor_background
= tattr
| tchar
;
2576 dispcurs
= disptext
+ idx
;
2579 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2582 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2584 /* Special hack for VT100 Linedraw glyphs */
2585 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2586 && tchar
<= 0xBD) break_run
= TRUE
;
2588 if (!dbcs_screenfont
&& !dirty_line
) {
2589 if ((tchar
| tattr
) == disptext
[idx
])
2591 else if (!dirty_run
&& ccount
== 1)
2596 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2597 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2603 if (dbcs_screenfont
)
2604 last_run_dirty
= dirty_run
;
2605 dirty_run
= dirty_line
;
2608 if ((tchar
| tattr
) != disptext
[idx
])
2610 ch
[ccount
++] = (char) tchar
;
2611 disptext
[idx
] = tchar
| tattr
;
2613 /* If it's a wide char step along to the next one. */
2614 if (tattr
& ATTR_WIDE
) {
2618 /* Cursor is here ? Ouch! */
2619 if (i
== our_curs_y
&& j
== curs
.x
) {
2620 cursor_background
= *d
;
2621 dispcurs
= disptext
+ idx
;
2623 if (disptext
[idx
] != *d
)
2629 if (dirty_run
&& ccount
> 0) {
2630 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2634 /* Cursor on this line ? (and changed) */
2635 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2636 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2637 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2638 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2645 * Flick the switch that says if blinking things should be shown or hidden.
2648 void term_blink(int flg
)
2650 static long last_blink
= 0;
2651 static long last_tblink
= 0;
2652 long now
, blink_diff
;
2654 now
= GetTickCount();
2655 blink_diff
= now
- last_tblink
;
2657 /* Make sure the text blinks no more than 2Hz */
2658 if (blink_diff
< 0 || blink_diff
> 450) {
2660 tblinker
= !tblinker
;
2669 blink_diff
= now
- last_blink
;
2671 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2672 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2680 * Invalidate the whole screen so it will be repainted in full.
2682 void term_invalidate(void)
2686 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2687 disptext
[i
] = ATTR_INVALID
;
2691 * Paint the window in response to a WM_PAINT message.
2693 void term_paint(Context ctx
, int l
, int t
, int r
, int b
)
2695 int i
, j
, left
, top
, right
, bottom
;
2697 left
= l
/ font_width
;
2698 right
= (r
- 1) / font_width
;
2699 top
= t
/ font_height
;
2700 bottom
= (b
- 1) / font_height
;
2701 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2702 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2703 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2704 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2706 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2707 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2710 /* This should happen soon enough, also for some reason it sometimes
2711 * fails to actually do anything when re-sizing ... painting the wrong
2713 do_paint (ctx, FALSE);
2718 * Attempt to scroll the scrollback. The second parameter gives the
2719 * position we want to scroll to; the first is +1 to denote that
2720 * this position is relative to the beginning of the scrollback, -1
2721 * to denote it is relative to the end, and 0 to denote that it is
2722 * relative to the current position.
2724 void term_scroll(int rel
, int where
)
2726 int sbtop
= -count234(scrollback
);
2728 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2729 if (disptop
< sbtop
)
2737 static void clipme(pos top
, pos bottom
)
2740 wchar_t *wbptr
; /* where next char goes within workbuf */
2741 int wblen
= 0; /* workbuf len */
2742 int buflen
; /* amount of memory allocated to workbuf */
2744 buflen
= 5120; /* Default size */
2745 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2746 wbptr
= workbuf
; /* start filling here */
2748 while (poslt(top
, bottom
)) {
2750 unsigned long *ldata
= lineptr(top
.y
);
2756 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2757 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2758 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2759 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2760 && poslt(top
, nlpos
))
2762 if (poslt(nlpos
, bottom
))
2765 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2768 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2770 wchar_t cbuf
[16], *p
;
2771 int uc
= (ldata
[top
.x
] & 0xFFFF);
2774 if (uc
== UCSWIDE
) {
2779 switch (uc
& CSET_MASK
) {
2782 uc
= unitab_xterm
[uc
& 0xFF];
2786 uc
= unitab_line
[uc
& 0xFF];
2789 uc
= unitab_scoacs
[uc
&0xFF];
2792 switch (uc
& CSET_MASK
) {
2794 uc
= unitab_font
[uc
& 0xFF];
2797 uc
= unitab_oemcp
[uc
& 0xFF];
2801 set
= (uc
& CSET_MASK
);
2802 c
= (uc
& CHAR_MASK
);
2806 if (DIRECT_FONT(uc
)) {
2807 if (c
>= ' ' && c
!= 0x7F) {
2808 unsigned char buf
[4];
2811 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
2813 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
2814 rv
= MultiByteToWideChar(font_codepage
,
2815 0, buf
, 2, wbuf
, 4);
2819 rv
= MultiByteToWideChar(font_codepage
,
2820 0, buf
, 1, wbuf
, 4);
2824 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
2831 for (p
= cbuf
; *p
; p
++) {
2832 /* Enough overhead for trailing NL and nul */
2833 if (wblen
>= buflen
- 16) {
2836 sizeof(wchar_t) * (buflen
+= 100));
2837 wbptr
= workbuf
+ wblen
;
2846 for (i
= 0; i
< sel_nl_sz
; i
++) {
2848 *wbptr
++ = sel_nl
[i
];
2856 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
2857 if (buflen
> 0) /* indicates we allocated this buffer */
2861 void term_copyall(void)
2864 top
.y
= -count234(scrollback
);
2870 * The wordness array is mainly for deciding the disposition of the US-ASCII
2873 static int wordtype(int uc
)
2876 int start
, end
, ctype
;
2877 } *wptr
, ucs_words
[] = {
2883 0x037e, 0x037e, 1}, /* Greek question mark */
2885 0x0387, 0x0387, 1}, /* Greek ano teleia */
2887 0x055a, 0x055f, 1}, /* Armenian punctuation */
2889 0x0589, 0x0589, 1}, /* Armenian full stop */
2891 0x0700, 0x070d, 1}, /* Syriac punctuation */
2893 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2895 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2897 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2899 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2901 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2903 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2905 0x2000, 0x200a, 0}, /* Various spaces */
2907 0x2070, 0x207f, 2}, /* superscript */
2909 0x2080, 0x208f, 2}, /* subscript */
2911 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2913 0x3000, 0x3000, 0}, /* ideographic space */
2915 0x3001, 0x3020, 1}, /* ideographic punctuation */
2917 0x303f, 0x309f, 3}, /* Hiragana */
2919 0x30a0, 0x30ff, 3}, /* Katakana */
2921 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2923 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2925 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2927 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2929 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2931 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2933 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2935 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2937 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2942 uc
&= (CSET_MASK
| CHAR_MASK
);
2944 switch (uc
& CSET_MASK
) {
2946 uc
= unitab_xterm
[uc
& 0xFF];
2949 uc
= unitab_line
[uc
& 0xFF];
2952 uc
= unitab_scoacs
[uc
&0xFF];
2955 switch (uc
& CSET_MASK
) {
2957 uc
= unitab_font
[uc
& 0xFF];
2960 uc
= unitab_oemcp
[uc
& 0xFF];
2965 return wordness
[uc
];
2967 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
2968 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
2976 * Spread the selection outwards according to the selection mode.
2978 static pos
sel_spread_half(pos p
, int dir
)
2980 unsigned long *ldata
;
2983 ldata
= lineptr(p
.y
);
2988 * In this mode, every character is a separate unit, except
2989 * for runs of spaces at the end of a non-wrapping line.
2991 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2992 unsigned long *q
= ldata
+ cols
;
2993 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
2995 if (q
== ldata
+ cols
)
2997 if (p
.x
>= q
- ldata
)
2998 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
3003 * In this mode, the units are maximal runs of characters
3004 * whose `wordness' has the same value.
3006 wvalue
= wordtype(ldata
[p
.x
]);
3008 while (p
.x
< cols
&& wordtype(ldata
[p
.x
+ 1]) == wvalue
)
3011 while (p
.x
> 0 && wordtype(ldata
[p
.x
- 1]) == wvalue
)
3017 * In this mode, every line is a unit.
3019 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3025 static void sel_spread(void)
3027 selstart
= sel_spread_half(selstart
, -1);
3029 selend
= sel_spread_half(selend
, +1);
3033 void term_do_paste(void)
3038 get_clip(&data
, &len
);
3043 sfree(paste_buffer
);
3044 paste_pos
= paste_hold
= paste_len
= 0;
3045 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3048 while (p
< data
+ len
) {
3049 while (p
< data
+ len
&&
3050 !(p
<= data
+ len
- sel_nl_sz
&&
3051 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3056 for (i
= 0; i
< p
- q
; i
++) {
3057 paste_buffer
[paste_len
++] = q
[i
];
3061 if (p
<= data
+ len
- sel_nl_sz
&&
3062 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3063 paste_buffer
[paste_len
++] = '\r';
3069 /* Assume a small paste will be OK in one go. */
3070 if (paste_len
< 256) {
3071 luni_send(paste_buffer
, paste_len
);
3073 sfree(paste_buffer
);
3075 paste_pos
= paste_hold
= paste_len
= 0;
3078 get_clip(NULL
, NULL
);
3081 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3082 int shift
, int ctrl
)
3085 unsigned long *ldata
;
3086 int raw_mouse
= xterm_mouse
&& !(cfg
.mouse_override
&& shift
);
3090 if (a
== MA_DRAG
&& !raw_mouse
)
3095 if (a
== MA_DRAG
&& !raw_mouse
)
3108 selpoint
.y
= y
+ disptop
;
3110 ldata
= lineptr(selpoint
.y
);
3111 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3115 int encstate
= 0, r
, c
;
3117 static int is_down
= 0;
3121 encstate
= 0x20; /* left button down */
3132 case MBT_WHEEL_DOWN
:
3135 default: break; /* placate gcc warning about enum use */
3139 if (xterm_mouse
== 1)
3152 default: break; /* placate gcc warning about enum use */
3161 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3162 ldisc_send(abuf
, 6);
3166 b
= translate_button(b
);
3168 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3170 selstate
= ABOUT_TO
;
3171 selanchor
= selpoint
;
3173 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3175 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3176 selstate
= DRAGGING
;
3177 selstart
= selanchor
= selpoint
;
3181 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3182 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3183 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3185 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3186 if (posdiff(selpoint
, selstart
) <
3187 posdiff(selend
, selstart
) / 2) {
3191 selanchor
= selstart
;
3193 selstate
= DRAGGING
;
3195 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3196 selanchor
= selpoint
;
3197 selstate
= DRAGGING
;
3198 if (poslt(selpoint
, selanchor
)) {
3199 selstart
= selpoint
;
3203 selstart
= selanchor
;
3208 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3209 if (selstate
== DRAGGING
) {
3211 * We've completed a selection. We now transfer the
3212 * data to the clipboard.
3214 clipme(selstart
, selend
);
3215 selstate
= SELECTED
;
3217 selstate
= NO_SELECTION
;
3218 } else if (b
== MBT_PASTE
3219 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3230 sfree(paste_buffer
);
3237 static long last_paste
= 0;
3238 long now
, paste_diff
;
3243 /* Don't wait forever to paste */
3245 now
= GetTickCount();
3246 paste_diff
= now
- last_paste
;
3247 if (paste_diff
>= 0 && paste_diff
< 450)
3252 while (paste_pos
< paste_len
) {
3254 while (n
+ paste_pos
< paste_len
) {
3255 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3258 luni_send(paste_buffer
+ paste_pos
, n
);
3261 if (paste_pos
< paste_len
) {
3266 sfree(paste_buffer
);
3271 static void deselect(void)
3273 selstate
= NO_SELECTION
;
3274 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3277 void term_deselect(void)
3283 int term_ldisc(int option
)
3285 if (option
== LD_ECHO
)
3286 return term_echoing
;
3287 if (option
== LD_EDIT
)
3288 return term_editing
;
3293 * from_backend(), to get data from the backend for the terminal.
3295 int from_backend(int is_stderr
, char *data
, int len
)
3298 if (inbuf_head
>= INBUF_SIZE
)
3300 inbuf
[inbuf_head
++] = *data
++;
3304 * We process all stdout/stderr data immediately we receive it,
3305 * and don't return until it's all gone. Therefore, there's no
3306 * reason at all to return anything other than zero from this
3309 * This is a slightly suboptimal way to deal with SSH2 - in
3310 * principle, the window mechanism would allow us to continue
3311 * to accept data on forwarded ports and X connections even
3312 * while the terminal processing was going slowly - but we
3313 * can't do the 100% right thing without moving the terminal
3314 * processing into a separate thread, and that might hurt
3315 * portability. So we manage stdout buffering the old SSH1 way:
3316 * if the terminal processing goes slowly, the whole SSH
3317 * connection stops accepting data until it's ready.
3319 * In practice, I can't imagine this causing serious trouble.
3325 * Log session traffic.
3327 void logtraffic(unsigned char c
, int logmode
)
3329 if (cfg
.logtype
> 0) {
3330 if (cfg
.logtype
== logmode
) {
3331 /* deferred open file from pgm start? */
3340 void settimstr(char *ta
, int no_sec
);
3341 char *subslfcode(char *dest
, char *src
, char *dstrt
);
3342 char *stpncpy(char *dst
, const char *src
, size_t maxlen
);
3344 char currlogfilename
[FILENAME_MAX
];
3346 /* open log file append/overwrite mode */
3356 sprintf(writemod
, "wb"); /* default to rewrite */
3359 tm
= *localtime(&t
);
3361 /* substitute special codes in file name */
3362 xlatlognam(currlogfilename
,cfg
.logfilename
,cfg
.host
, &tm
);
3364 lgfp
= fopen(currlogfilename
, "r"); /* file already present? */
3368 i
= askappend(currlogfilename
);
3370 writemod
[0] = 'a'; /* set append mode */
3371 else if (i
== 0) { /* cancelled */
3373 cfg
.logtype
= 0; /* disable logging */
3378 lgfp
= fopen(currlogfilename
, writemod
);
3379 if (lgfp
) { /* enter into event log */
3380 sprintf(buf
, "%s session log (%s mode) to file : ",
3381 (writemod
[0] == 'a') ?
"Appending" : "Writing new",
3382 (cfg
.logtype
== LGTYP_ASCII ?
"ASCII" :
3383 cfg
.logtype
== LGTYP_DEBUG ?
"raw" : "<ukwn>"));
3384 /* Make sure we do not exceed the output buffer size */
3385 strncat(buf
, currlogfilename
, 128);
3386 buf
[strlen(buf
)] = '\0';
3389 /* --- write header line into log file */
3390 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp
);
3391 strftime(buf
, 24, "%Y.%m.%d %H:%M:%S", &tm
);
3393 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp
);
3397 void logfclose(void)
3406 * translate format codes into time/date strings
3407 * and insert them into log file name
3409 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3411 static void xlatlognam(char *d
, char *s
, char *hostname
, struct tm
*tm
) {
3412 char buf
[10], *bufp
;
3414 char *ds
= d
; /* save start pos. */
3415 int len
= FILENAME_MAX
-1;
3418 /* Let (bufp, len) be the string to append. */
3419 bufp
= buf
; /* don't usually override this */
3423 if (*s
) switch (c
= *s
++, tolower(c
)) {
3425 size
= strftime(buf
, sizeof(buf
), "%Y", tm
);
3428 size
= strftime(buf
, sizeof(buf
), "%m", tm
);
3431 size
= strftime(buf
, sizeof(buf
), "%d", tm
);
3434 size
= strftime(buf
, sizeof(buf
), "%H%M%S", tm
);
3438 size
= strlen(bufp
);
3452 memcpy(d
, bufp
, size
);