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
);
506 static void swap_screen(int which
)
511 if (which
== alt_which
)
538 wrapnext
= alt_wnext
;
550 sco_acs
= alt_sco_acs
;
557 * Update the scroll bar.
559 static void update_sbar(void)
563 nscroll
= count234(scrollback
);
565 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
569 * Check whether the region bounded by the two pointers intersects
570 * the scroll region, and de-select the on-screen selection if so.
572 static void check_selection(pos from
, pos to
)
574 if (poslt(from
, selend
) && poslt(selstart
, to
))
579 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
580 * for backward.) `sb' is TRUE if the scrolling is permitted to
581 * affect the scrollback buffer.
583 * NB this function invalidates all pointers into lines of the
584 * screen data structures. In particular, you MUST call fix_cpos
585 * after calling scroll() and before doing anything else that
586 * uses the cpos shortcut pointer.
588 static void scroll(int topline
, int botline
, int lines
, int sb
)
590 unsigned long *line
, *line2
;
593 if (topline
!= 0 || alt_which
!= 0)
598 line
= delpos234(screen
, botline
);
599 line
= resizeline(line
, cols
);
600 for (i
= 0; i
< cols
; i
++)
601 line
[i
+ 1] = erase_char
;
603 addpos234(screen
, line
, topline
);
605 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
607 if (selstart
.y
> botline
) {
608 selstart
.y
= botline
;
612 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
614 if (selend
.y
> botline
) {
624 line
= delpos234(screen
, topline
);
625 if (sb
&& savelines
> 0) {
626 int sblen
= count234(scrollback
);
628 * We must add this line to the scrollback. We'll
629 * remove a line from the top of the scrollback to
630 * replace it, or allocate a new one if the
631 * scrollback isn't full.
633 if (sblen
== savelines
) {
634 sblen
--, line2
= delpos234(scrollback
, 0);
636 line2
= smalloc(TSIZE
* (cols
+ 2));
639 addpos234(scrollback
, line
, sblen
);
643 * If the user is currently looking at part of the
644 * scrollback, and they haven't enabled any options
645 * that are going to reset the scrollback as a
646 * result of this movement, then the chances are
647 * they'd like to keep looking at the same line. So
648 * we move their viewpoint at the same rate as the
649 * scroll, at least until their viewpoint hits the
650 * top end of the scrollback buffer, at which point
651 * we don't have the choice any more.
653 * Thanks to Jan Holmen Holsten for the idea and
654 * initial implementation.
656 if (disptop
> -savelines
&& disptop
< 0)
659 line
= resizeline(line
, cols
);
660 for (i
= 0; i
< cols
; i
++)
661 line
[i
+ 1] = erase_char
;
663 addpos234(screen
, line
, botline
);
666 * If the selection endpoints move into the scrollback,
667 * we keep them moving until they hit the top. However,
668 * of course, if the line _hasn't_ moved into the
669 * scrollback then we don't do this, and cut them off
670 * at the top of the scroll region.
672 * This applies to selstart and selend (for an existing
673 * selection), and also selanchor (for one being
674 * selected as we speak).
676 seltop
= sb ?
-savelines
: 0;
678 if (selstart
.y
>= seltop
&& selstart
.y
<= botline
) {
680 if (selstart
.y
< seltop
) {
685 if (selend
.y
>= seltop
&& selend
.y
<= botline
) {
687 if (selend
.y
< seltop
) {
692 if (selanchor
.y
>= seltop
&& selanchor
.y
<= botline
) {
694 if (selanchor
.y
< seltop
) {
695 selanchor
.y
= seltop
;
706 * Move the cursor to a given position, clipping at boundaries. We
707 * may or may not want to clip at the scroll margin: marg_clip is 0
708 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
709 * even _being_ outside the margins.
711 static void move(int x
, int y
, int marg_clip
)
718 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
720 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
734 * Save or restore the cursor and SGR mode.
736 static void save_cursor(int save
)
740 save_attr
= curr_attr
;
743 save_csattr
= cset_attr
[cset
];
744 save_sco_acs
= sco_acs
;
747 /* Make sure the window hasn't shrunk since the save */
753 curr_attr
= save_attr
;
756 cset_attr
[cset
] = save_csattr
;
757 sco_acs
= save_sco_acs
;
760 erase_char
= (' ' | ATTR_ASCII
|
761 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
766 * Erase a large portion of the screen: the whole screen, or the
767 * whole line, or parts thereof.
769 static void erase_lots(int line_only
, int from_begin
, int to_end
)
773 unsigned long *ldata
;
795 check_selection(start
, end
);
797 /* Clear screen also forces a full window redraw, just in case. */
798 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
801 ldata
= lineptr(start
.y
);
802 while (poslt(start
, end
)) {
803 if (start
.x
== cols
&& !erase_lattr
)
804 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
806 ldata
[start
.x
] = erase_char
;
807 if (incpos(start
) && start
.y
< rows
)
808 ldata
= lineptr(start
.y
);
813 * Insert or delete characters within the current line. n is +ve if
814 * insertion is desired, and -ve for deletion.
816 static void insch(int n
)
818 int dir
= (n
< 0 ?
-1 : +1);
821 unsigned long *ldata
;
823 n
= (n
< 0 ?
-n
: n
);
824 if (n
> cols
- curs
.x
)
826 m
= cols
- curs
.x
- n
;
828 cursplus
.x
= curs
.x
+ n
;
829 check_selection(curs
, cursplus
);
830 ldata
= lineptr(curs
.y
);
832 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
834 ldata
[curs
.x
+ m
++] = erase_char
;
836 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
838 ldata
[curs
.x
+ n
] = erase_char
;
843 * Toggle terminal mode `mode' to state `state'. (`query' indicates
844 * whether the mode is a DEC private one or a normal one.)
846 static void toggle_mode(int mode
, int query
, int state
)
852 case 1: /* application cursor keys */
853 app_cursor_keys
= state
;
855 case 2: /* VT52 mode */
858 blink_is_real
= FALSE
;
861 blink_is_real
= cfg
.blinktext
;
864 case 3: /* 80/132 columns */
866 request_resize(state ?
132 : 80, rows
);
869 case 5: /* reverse video */
871 * Toggle reverse video. If we receive an OFF within the
872 * visual bell timeout period after an ON, we trigger an
873 * effective visual bell, so that ESC[?5hESC[?5l will
874 * always be an actually _visible_ visual bell.
876 ticks
= GetTickCount();
877 if (rvideo
&& !state
&& /* we're turning it off */
878 ticks
< rvbell_timeout
) { /* and it's not long since it was turned on */
879 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
880 if (vbell_timeout
< rvbell_timeout
) /* don't move vbell end forward */
881 vbell_timeout
= rvbell_timeout
; /* vbell end is at least then */
882 } else if (!rvideo
&& state
) {
883 /* This is an ON, so we notice the time and save it. */
884 rvbell_timeout
= ticks
+ VBELL_TIMEOUT
;
887 seen_disp_event
= TRUE
;
891 case 6: /* DEC origin mode */
894 case 7: /* auto wrap */
897 case 8: /* auto key repeat */
900 case 10: /* set local edit mode */
901 term_editing
= state
;
902 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
904 case 25: /* enable/disable cursor */
905 compatibility2(OTHER
, VT220
);
907 seen_disp_event
= TRUE
;
909 case 47: /* alternate screen */
910 compatibility(OTHER
);
915 case 1000: /* xterm mouse 1 */
916 xterm_mouse
= state ?
1 : 0;
917 set_raw_mouse_mode(state
);
919 case 1002: /* xterm mouse 2 */
920 xterm_mouse
= state ?
2 : 0;
921 set_raw_mouse_mode(state
);
925 case 4: /* set insert mode */
926 compatibility(VT102
);
929 case 12: /* set echo mode */
930 term_echoing
= !state
;
931 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
933 case 20: /* Return sends ... */
934 cr_lf_return
= state
;
936 case 34: /* Make cursor BIG */
937 compatibility2(OTHER
, VT220
);
943 * Process an OSC sequence: set window title or icon name.
945 static void do_osc(void)
949 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
951 osc_string
[osc_strlen
] = '\0';
952 switch (esc_args
[0]) {
955 set_icon(osc_string
);
956 if (esc_args
[0] == 1)
958 /* fall through: parameter 0 means set both */
961 set_title(osc_string
);
968 * Remove everything currently in `inbuf' and stick it up on the
969 * in-memory display. There's a big state machine in here to
970 * process escape sequences...
977 * Optionally log the session traffic to a file. Useful for
978 * debugging and possibly also useful for actual logging.
980 if (cfg
.logtype
== LGTYP_DEBUG
)
981 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
982 logtraffic((unsigned char) inbuf
[inbuf_reap
], LGTYP_DEBUG
);
985 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
986 c
= inbuf
[inbuf_reap
];
988 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
989 * be able to display 8-bit characters, but I'll let that go 'cause
993 /* First see about all those translations. */
994 if (termstate
== TOPLEVEL
) {
999 /* UTF-8 must be stateless so we ignore iso2022. */
1000 if (unitab_ctrl
[c
] != 0xFF)
1002 else c
= ((unsigned char)c
) | ATTR_ASCII
;
1004 } else if ((c
& 0xe0) == 0xc0) {
1005 utf_size
= utf_state
= 1;
1006 utf_char
= (c
& 0x1f);
1007 } else if ((c
& 0xf0) == 0xe0) {
1008 utf_size
= utf_state
= 2;
1009 utf_char
= (c
& 0x0f);
1010 } else if ((c
& 0xf8) == 0xf0) {
1011 utf_size
= utf_state
= 3;
1012 utf_char
= (c
& 0x07);
1013 } else if ((c
& 0xfc) == 0xf8) {
1014 utf_size
= utf_state
= 4;
1015 utf_char
= (c
& 0x03);
1016 } else if ((c
& 0xfe) == 0xfc) {
1017 utf_size
= utf_state
= 5;
1018 utf_char
= (c
& 0x01);
1029 if ((c
& 0xC0) != 0x80) {
1035 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
1041 /* Is somebody trying to be evil! */
1043 (c
< 0x800 && utf_size
>= 2) ||
1044 (c
< 0x10000 && utf_size
>= 3) ||
1045 (c
< 0x200000 && utf_size
>= 4) ||
1046 (c
< 0x4000000 && utf_size
>= 5))
1049 /* Unicode line separator and paragraph separator are CR-LF */
1050 if (c
== 0x2028 || c
== 0x2029)
1053 /* High controls are probably a Baaad idea too. */
1057 /* The UTF-16 surrogates are not nice either. */
1058 /* The standard give the option of decoding these:
1059 * I don't want to! */
1060 if (c
>= 0xD800 && c
< 0xE000)
1063 /* ISO 10646 characters now limited to UTF-16 range. */
1067 /* This is currently a TagPhobic application.. */
1068 if (c
>= 0xE0000 && c
<= 0xE007F)
1071 /* U+FEFF is best seen as a null. */
1074 /* But U+FFFE is an error. */
1075 if (c
== 0xFFFE || c
== 0xFFFF)
1078 /* Oops this is a 16bit implementation */
1083 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1085 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1087 if (sco_acs
== 2) c
^= 0x80;
1090 switch (cset_attr
[cset
]) {
1092 * Linedraw characters are different from 'ESC ( B'
1093 * only for a small range. For ones outside that
1094 * range, make sure we use the same font as well as
1095 * the same encoding.
1098 if (unitab_ctrl
[c
] != 0xFF)
1101 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1105 /* If UK-ASCII, make the '#' a LineDraw Pound */
1107 c
= '}' | ATTR_LINEDRW
;
1110 /*FALLTHROUGH*/ case ATTR_ASCII
:
1111 if (unitab_ctrl
[c
] != 0xFF)
1114 c
= ((unsigned char) c
) | ATTR_ASCII
;
1117 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1123 /* How about C1 controls ? */
1124 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1125 has_compat(VT220
)) {
1126 termstate
= SEEN_ESC
;
1128 c
= '@' + (c
& 0x1F);
1131 /* Or the GL control. */
1132 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1133 if (curs
.x
&& !wrapnext
)
1137 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1139 /* Or normal C0 controls. */
1140 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1142 case '\005': /* terminal type query */
1143 /* Strictly speaking this is VT100 but a VT100 defaults to
1144 * no response. Other terminals respond at their option.
1146 * Don't put a CR in the default string as this tends to
1147 * upset some weird software.
1149 * An xterm returns "xterm" (5 characters)
1151 compatibility(ANSIMIN
);
1153 char abuf
[256], *s
, *d
;
1155 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1157 if (*s
>= 'a' && *s
<= 'z')
1158 *d
++ = (*s
- ('a' - 1));
1159 else if ((*s
>= '@' && *s
<= '_') ||
1160 *s
== '?' || (*s
& 0x80))
1165 } else if (*s
== '^') {
1170 lpage_send(CP_ACP
, abuf
, d
- abuf
);
1175 struct beeptime
*newbeep
;
1178 ticks
= GetTickCount();
1180 if (!beep_overloaded
) {
1181 newbeep
= smalloc(sizeof(struct beeptime
));
1182 newbeep
->ticks
= ticks
;
1183 newbeep
->next
= NULL
;
1187 beeptail
->next
= newbeep
;
1193 * Throw out any beeps that happened more than
1197 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1198 struct beeptime
*tmp
= beephead
;
1199 beephead
= tmp
->next
;
1206 if (cfg
.bellovl
&& beep_overloaded
&&
1207 ticks
- lastbeep
>= cfg
.bellovl_s
) {
1209 * If we're currently overloaded and the
1210 * last beep was more than s seconds ago,
1211 * leave overload mode.
1213 beep_overloaded
= FALSE
;
1214 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1215 nbeeps
>= cfg
.bellovl_n
) {
1217 * Now, if we have n or more beeps
1218 * remaining in the queue, go into overload
1221 beep_overloaded
= TRUE
;
1226 * Perform an actual beep if we're not overloaded.
1228 if (!cfg
.bellovl
|| !beep_overloaded
) {
1230 if (cfg
.beep
== BELL_VISUAL
) {
1232 vbell_timeout
= ticks
+ VBELL_TIMEOUT
;
1240 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1241 else if (curs
.x
== 0 && curs
.y
> 0)
1242 curs
.x
= cols
- 1, curs
.y
--;
1248 seen_disp_event
= TRUE
;
1251 compatibility(VT100
);
1255 compatibility(VT100
);
1260 termstate
= VT52_ESC
;
1262 compatibility(ANSIMIN
);
1263 termstate
= SEEN_ESC
;
1271 seen_disp_event
= TRUE
;
1273 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1276 if (has_compat(SCOANSI
)) {
1278 erase_lots(FALSE
, FALSE
, TRUE
);
1281 seen_disp_event
= 1;
1285 compatibility(VT100
);
1287 if (curs
.y
== marg_b
)
1288 scroll(marg_t
, marg_b
, 1, TRUE
);
1289 else if (curs
.y
< rows
- 1)
1295 seen_disp_event
= 1;
1297 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1301 pos old_curs
= curs
;
1302 unsigned long *ldata
= lineptr(curs
.y
);
1306 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1308 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1309 if (curs
.x
>= cols
/ 2)
1310 curs
.x
= cols
/ 2 - 1;
1317 check_selection(old_curs
, curs
);
1319 seen_disp_event
= TRUE
;
1323 switch (termstate
) {
1325 /* Only graphic characters get this far, ctrls are stripped above */
1326 if (wrapnext
&& wrap
) {
1327 cpos
[1] |= LATTR_WRAPPED
;
1328 if (curs
.y
== marg_b
)
1329 scroll(marg_t
, marg_b
, 1, TRUE
);
1330 else if (curs
.y
< rows
- 1)
1338 if (selstate
!= NO_SELECTION
) {
1339 pos cursplus
= curs
;
1341 check_selection(curs
, cursplus
);
1343 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1344 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1346 extern int wcwidth(wchar_t ucs
);
1351 width
= wcwidth((wchar_t) c
);
1354 *cpos
++ = c
| curr_attr
;
1355 if (++curs
.x
== cols
) {
1356 *cpos
|= LATTR_WRAPPED
;
1357 if (curs
.y
== marg_b
)
1358 scroll(marg_t
, marg_b
, 1, TRUE
);
1359 else if (curs
.y
< rows
- 1)
1364 *cpos
++ = UCSWIDE
| curr_attr
;
1367 *cpos
++ = c
| curr_attr
;
1374 if (curs
.x
== cols
) {
1378 if (wrap
&& vt52_mode
) {
1379 cpos
[1] |= LATTR_WRAPPED
;
1380 if (curs
.y
== marg_b
)
1381 scroll(marg_t
, marg_b
, 1, TRUE
);
1382 else if (curs
.y
< rows
- 1)
1389 seen_disp_event
= 1;
1394 * This state is virtually identical to SEEN_ESC, with the
1395 * exception that we have an OSC sequence in the pipeline,
1396 * and _if_ we see a backslash, we process it.
1400 termstate
= TOPLEVEL
;
1403 /* else fall through */
1405 if (c
>= ' ' && c
<= '/') {
1412 termstate
= TOPLEVEL
;
1413 switch (ANSI(c
, esc_query
)) {
1414 case '[': /* enter CSI mode */
1415 termstate
= SEEN_CSI
;
1417 esc_args
[0] = ARG_DEFAULT
;
1420 case ']': /* xterm escape sequences */
1421 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1422 compatibility(OTHER
);
1423 termstate
= SEEN_OSC
;
1426 case '7': /* save cursor */
1427 compatibility(VT100
);
1430 case '8': /* restore cursor */
1431 compatibility(VT100
);
1433 seen_disp_event
= TRUE
;
1436 compatibility(VT100
);
1437 app_keypad_keys
= TRUE
;
1440 compatibility(VT100
);
1441 app_keypad_keys
= FALSE
;
1443 case 'D': /* exactly equivalent to LF */
1444 compatibility(VT100
);
1445 if (curs
.y
== marg_b
)
1446 scroll(marg_t
, marg_b
, 1, TRUE
);
1447 else if (curs
.y
< rows
- 1)
1451 seen_disp_event
= TRUE
;
1453 case 'E': /* exactly equivalent to CR-LF */
1454 compatibility(VT100
);
1456 if (curs
.y
== marg_b
)
1457 scroll(marg_t
, marg_b
, 1, TRUE
);
1458 else if (curs
.y
< rows
- 1)
1462 seen_disp_event
= TRUE
;
1464 case 'M': /* reverse index - backwards LF */
1465 compatibility(VT100
);
1466 if (curs
.y
== marg_t
)
1467 scroll(marg_t
, marg_b
, -1, TRUE
);
1468 else if (curs
.y
> 0)
1472 seen_disp_event
= TRUE
;
1474 case 'Z': /* terminal type query */
1475 compatibility(VT100
);
1476 ldisc_send(id_string
, strlen(id_string
));
1478 case 'c': /* restore power-on settings */
1479 compatibility(VT100
);
1482 request_resize(80, rows
);
1487 seen_disp_event
= TRUE
;
1489 case 'H': /* set a tab */
1490 compatibility(VT100
);
1491 tabs
[curs
.x
] = TRUE
;
1494 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1495 compatibility(VT100
);
1497 unsigned long *ldata
;
1501 for (i
= 0; i
< rows
; i
++) {
1503 for (j
= 0; j
< cols
; j
++)
1504 ldata
[j
] = ATTR_DEFAULT
| 'E';
1508 seen_disp_event
= TRUE
;
1509 scrtop
.x
= scrtop
.y
= 0;
1512 check_selection(scrtop
, scrbot
);
1516 case ANSI('3', '#'):
1517 case ANSI('4', '#'):
1518 case ANSI('5', '#'):
1519 case ANSI('6', '#'):
1520 compatibility(VT100
);
1522 unsigned long nlattr
;
1523 unsigned long *ldata
;
1524 switch (ANSI(c
, esc_query
)) {
1525 case ANSI('3', '#'):
1528 case ANSI('4', '#'):
1531 case ANSI('5', '#'):
1532 nlattr
= LATTR_NORM
;
1534 default: /* spiritually case ANSI('6', '#'): */
1535 nlattr
= LATTR_WIDE
;
1538 ldata
= lineptr(curs
.y
);
1539 ldata
[cols
] &= ~LATTR_MODE
;
1540 ldata
[cols
] |= nlattr
;
1544 case ANSI('A', '('):
1545 compatibility(VT100
);
1546 cset_attr
[0] = ATTR_GBCHR
;
1548 case ANSI('B', '('):
1549 compatibility(VT100
);
1550 cset_attr
[0] = ATTR_ASCII
;
1552 case ANSI('0', '('):
1553 compatibility(VT100
);
1554 cset_attr
[0] = ATTR_LINEDRW
;
1556 case ANSI('U', '('):
1557 compatibility(OTHER
);
1558 cset_attr
[0] = ATTR_SCOACS
;
1561 case ANSI('A', ')'):
1562 compatibility(VT100
);
1563 cset_attr
[1] = ATTR_GBCHR
;
1565 case ANSI('B', ')'):
1566 compatibility(VT100
);
1567 cset_attr
[1] = ATTR_ASCII
;
1569 case ANSI('0', ')'):
1570 compatibility(VT100
);
1571 cset_attr
[1] = ATTR_LINEDRW
;
1573 case ANSI('U', ')'):
1574 compatibility(OTHER
);
1575 cset_attr
[1] = ATTR_SCOACS
;
1578 case ANSI('8', '%'): /* Old Linux code */
1579 case ANSI('G', '%'):
1580 compatibility(OTHER
);
1583 case ANSI('@', '%'):
1584 compatibility(OTHER
);
1590 termstate
= TOPLEVEL
; /* default */
1592 if (esc_nargs
<= ARGS_MAX
) {
1593 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1594 esc_args
[esc_nargs
- 1] = 0;
1595 esc_args
[esc_nargs
- 1] =
1596 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1598 termstate
= SEEN_CSI
;
1599 } else if (c
== ';') {
1600 if (++esc_nargs
<= ARGS_MAX
)
1601 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1602 termstate
= SEEN_CSI
;
1603 } else if (c
< '@') {
1610 termstate
= SEEN_CSI
;
1612 switch (ANSI(c
, esc_query
)) {
1613 case 'A': /* move up N lines */
1614 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1615 seen_disp_event
= TRUE
;
1617 case 'e': /* move down N lines */
1618 compatibility(ANSI
);
1621 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1622 seen_disp_event
= TRUE
;
1624 case ANSI('c', '>'): /* report xterm version */
1625 compatibility(OTHER
);
1626 /* this reports xterm version 136 so that VIM can
1627 use the drag messages from the mouse reporting */
1628 ldisc_send("\033[>0;136;0c", 11);
1630 case 'a': /* move right N cols */
1631 compatibility(ANSI
);
1634 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1635 seen_disp_event
= TRUE
;
1637 case 'D': /* move left N cols */
1638 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1639 seen_disp_event
= TRUE
;
1641 case 'E': /* move down N lines and CR */
1642 compatibility(ANSI
);
1643 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1644 seen_disp_event
= TRUE
;
1646 case 'F': /* move up N lines and CR */
1647 compatibility(ANSI
);
1648 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1649 seen_disp_event
= TRUE
;
1652 case '`': /* set horizontal posn */
1653 compatibility(ANSI
);
1654 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1655 seen_disp_event
= TRUE
;
1657 case 'd': /* set vertical posn */
1658 compatibility(ANSI
);
1660 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1663 seen_disp_event
= TRUE
;
1666 case 'f': /* set horz and vert posns at once */
1668 esc_args
[1] = ARG_DEFAULT
;
1669 move(def(esc_args
[1], 1) - 1,
1670 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1673 seen_disp_event
= TRUE
;
1675 case 'J': /* erase screen or parts of it */
1677 unsigned int i
= def(esc_args
[0], 0) + 1;
1680 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1683 seen_disp_event
= TRUE
;
1685 case 'K': /* erase line or parts of it */
1687 unsigned int i
= def(esc_args
[0], 0) + 1;
1690 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1692 seen_disp_event
= TRUE
;
1694 case 'L': /* insert lines */
1695 compatibility(VT102
);
1696 if (curs
.y
<= marg_b
)
1697 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1700 seen_disp_event
= TRUE
;
1702 case 'M': /* delete lines */
1703 compatibility(VT102
);
1704 if (curs
.y
<= marg_b
)
1705 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1708 seen_disp_event
= TRUE
;
1710 case '@': /* insert chars */
1711 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1712 compatibility(VT102
);
1713 insch(def(esc_args
[0], 1));
1714 seen_disp_event
= TRUE
;
1716 case 'P': /* delete chars */
1717 compatibility(VT102
);
1718 insch(-def(esc_args
[0], 1));
1719 seen_disp_event
= TRUE
;
1721 case 'c': /* terminal type query */
1722 compatibility(VT100
);
1723 /* This is the response for a VT102 */
1724 ldisc_send(id_string
, strlen(id_string
));
1726 case 'n': /* cursor position query */
1727 if (esc_args
[0] == 6) {
1729 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1731 ldisc_send(buf
, strlen(buf
));
1732 } else if (esc_args
[0] == 5) {
1733 ldisc_send("\033[0n", 4);
1736 case 'h': /* toggle modes to high */
1738 compatibility(VT100
);
1741 for (i
= 0; i
< esc_nargs
; i
++)
1742 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1745 case 'l': /* toggle modes to low */
1747 compatibility(VT100
);
1750 for (i
= 0; i
< esc_nargs
; i
++)
1751 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1754 case 'g': /* clear tabs */
1755 compatibility(VT100
);
1756 if (esc_nargs
== 1) {
1757 if (esc_args
[0] == 0) {
1758 tabs
[curs
.x
] = FALSE
;
1759 } else if (esc_args
[0] == 3) {
1761 for (i
= 0; i
< cols
; i
++)
1766 case 'r': /* set scroll margins */
1767 compatibility(VT100
);
1768 if (esc_nargs
<= 2) {
1770 top
= def(esc_args
[0], 1) - 1;
1771 bot
= (esc_nargs
<= 1
1773 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1776 /* VTTEST Bug 9 - if region is less than 2 lines
1777 * don't change region.
1779 if (bot
- top
> 0) {
1784 * I used to think the cursor should be
1785 * placed at the top of the newly marginned
1786 * area. Apparently not: VMS TPU falls over
1789 * Well actually it should for Origin mode - RDB
1791 curs
.y
= (dec_om ? marg_t
: 0);
1793 seen_disp_event
= TRUE
;
1797 case 'm': /* set graphics rendition */
1800 * A VT100 without the AVO only had one attribute, either
1801 * underline or reverse video depending on the cursor type,
1802 * this was selected by CSI 7m.
1805 * This is sometimes DIM, eg on the GIGI and Linux
1807 * This is sometimes INVIS various ANSI.
1809 * This like 22 disables BOLD, DIM and INVIS
1811 * The ANSI colours appear on any terminal that has colour
1812 * (obviously) but the interaction between sgr0 and the
1813 * colours varies but is usually related to the background
1814 * colour erase item.
1815 * The interaction between colour attributes and the mono
1816 * ones is also very implementation dependent.
1818 * The 39 and 49 attributes are likely to be unimplemented.
1821 for (i
= 0; i
< esc_nargs
; i
++) {
1822 switch (def(esc_args
[i
], 0)) {
1823 case 0: /* restore defaults */
1824 curr_attr
= ATTR_DEFAULT
;
1826 case 1: /* enable bold */
1827 compatibility(VT100AVO
);
1828 curr_attr
|= ATTR_BOLD
;
1830 case 21: /* (enable double underline) */
1831 compatibility(OTHER
);
1832 case 4: /* enable underline */
1833 compatibility(VT100AVO
);
1834 curr_attr
|= ATTR_UNDER
;
1836 case 5: /* enable blink */
1837 compatibility(VT100AVO
);
1838 curr_attr
|= ATTR_BLINK
;
1840 case 7: /* enable reverse video */
1841 curr_attr
|= ATTR_REVERSE
;
1843 case 10: /* SCO acs off */
1844 compatibility(SCOANSI
);
1846 case 11: /* SCO acs on */
1847 compatibility(SCOANSI
);
1849 case 12: /* SCO acs on flipped */
1850 compatibility(SCOANSI
);
1852 case 22: /* disable bold */
1853 compatibility2(OTHER
, VT220
);
1854 curr_attr
&= ~ATTR_BOLD
;
1856 case 24: /* disable underline */
1857 compatibility2(OTHER
, VT220
);
1858 curr_attr
&= ~ATTR_UNDER
;
1860 case 25: /* disable blink */
1861 compatibility2(OTHER
, VT220
);
1862 curr_attr
&= ~ATTR_BLINK
;
1864 case 27: /* disable reverse video */
1865 compatibility2(OTHER
, VT220
);
1866 curr_attr
&= ~ATTR_REVERSE
;
1877 curr_attr
&= ~ATTR_FGMASK
;
1879 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1881 case 39: /* default-foreground */
1882 curr_attr
&= ~ATTR_FGMASK
;
1883 curr_attr
|= ATTR_DEFFG
;
1894 curr_attr
&= ~ATTR_BGMASK
;
1896 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1898 case 49: /* default-background */
1899 curr_attr
&= ~ATTR_BGMASK
;
1900 curr_attr
|= ATTR_DEFBG
;
1905 erase_char
= (' ' | ATTR_ASCII
|
1907 (ATTR_FGMASK
| ATTR_BGMASK
)));
1910 case 's': /* save cursor */
1913 case 'u': /* restore cursor */
1915 seen_disp_event
= TRUE
;
1917 case 't': /* set page size - ie window height */
1919 * VT340/VT420 sequence DECSLPP, DEC only allows values
1920 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1921 * illegal values (eg first arg 1..9) for window changing
1924 compatibility(VT340TEXT
);
1926 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1927 request_resize(cols
, def(esc_args
[0], 24));
1932 compatibility(SCOANSI
);
1933 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
1936 seen_disp_event
= TRUE
;
1939 compatibility(SCOANSI
);
1940 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
1943 seen_disp_event
= TRUE
;
1945 case ANSI('|', '*'):
1946 /* VT420 sequence DECSNLS
1947 * Set number of lines on screen
1948 * VT420 uses VGA like hardware and can support any size in
1949 * reasonable range (24..49 AIUI) with no default specified.
1951 compatibility(VT420
);
1952 if (esc_nargs
== 1 && esc_args
[0] > 0) {
1953 request_resize(cols
, def(esc_args
[0], cfg
.height
));
1957 case ANSI('|', '$'):
1958 /* VT340/VT420 sequence DECSCPP
1959 * Set number of columns per page
1960 * Docs imply range is only 80 or 132, but I'll allow any.
1962 compatibility(VT340TEXT
);
1963 if (esc_nargs
<= 1) {
1964 request_resize(def(esc_args
[0], cfg
.width
), rows
);
1968 case 'X': /* write N spaces w/o moving cursor */
1969 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1970 compatibility(ANSIMIN
);
1972 int n
= def(esc_args
[0], 1);
1974 unsigned long *p
= cpos
;
1975 if (n
> cols
- curs
.x
)
1979 check_selection(curs
, cursplus
);
1982 seen_disp_event
= TRUE
;
1985 case 'x': /* report terminal characteristics */
1986 compatibility(VT100
);
1989 int i
= def(esc_args
[0], 0);
1990 if (i
== 0 || i
== 1) {
1991 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
1993 ldisc_send(buf
, 20);
1997 case 'Z': /* BackTab for xterm */
1998 compatibility(OTHER
);
2000 int i
= def(esc_args
[0], 1);
2001 pos old_curs
= curs
;
2003 for(;i
>0 && curs
.x
>0; i
--) {
2006 } while (curs
.x
>0 && !tabs
[curs
.x
]);
2009 check_selection(old_curs
, curs
);
2012 case ANSI('L', '='):
2013 compatibility(OTHER
);
2014 use_bce
= (esc_args
[0] <= 0);
2015 erase_char
= ERASE_CHAR
;
2017 erase_char
= (' ' | ATTR_ASCII
|
2019 (ATTR_FGMASK
| ATTR_BGMASK
)));
2021 case ANSI('E', '='):
2022 compatibility(OTHER
);
2023 blink_is_real
= (esc_args
[0] >= 1);
2025 case ANSI('p', '"'):
2026 /* Allow the host to make this emulator a 'perfect' VT102.
2027 * This first appeared in the VT220, but we do need to get
2028 * back to PuTTY mode so I won't check it.
2030 * The arg in 40..42,50 are a PuTTY extension.
2031 * The 2nd arg, 8bit vs 7bit is not checked.
2033 * Setting VT102 mode should also change the Fkeys to
2034 * generate PF* codes as a real VT102 has no Fkeys.
2035 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2038 * Note ESC c will NOT change this!
2041 switch (esc_args
[0]) {
2043 compatibility_level
&= ~TM_VTXXX
;
2044 compatibility_level
|= TM_VT102
;
2047 compatibility_level
&= ~TM_VTXXX
;
2048 compatibility_level
|= TM_VT220
;
2052 if (esc_args
[0] > 60 && esc_args
[0] < 70)
2053 compatibility_level
|= TM_VTXXX
;
2057 compatibility_level
&= TM_VTXXX
;
2060 compatibility_level
= TM_PUTTY
;
2063 compatibility_level
= TM_SCOANSI
;
2067 compatibility_level
= TM_PUTTY
;
2073 /* Change the response to CSI c */
2074 if (esc_args
[0] == 50) {
2077 strcpy(id_string
, "\033[?");
2078 for (i
= 1; i
< esc_nargs
; i
++) {
2080 strcat(id_string
, ";");
2081 sprintf(lbuf
, "%d", esc_args
[i
]);
2082 strcat(id_string
, lbuf
);
2084 strcat(id_string
, "c");
2087 /* Is this a good idea ?
2088 * Well we should do a soft reset at this point ...
2090 if (!has_compat(VT420
) && has_compat(VT100
)) {
2092 request_resize(132, 24);
2094 request_resize(80, 24);
2103 case 'P': /* Linux palette sequence */
2104 termstate
= SEEN_OSC_P
;
2107 case 'R': /* Linux palette reset */
2110 termstate
= TOPLEVEL
;
2112 case 'W': /* word-set */
2113 termstate
= SEEN_OSC_W
;
2126 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2130 * Grotty hack to support xterm and DECterm title
2131 * sequences concurrently.
2133 if (esc_args
[0] == 2) {
2137 /* else fall through */
2139 termstate
= OSC_STRING
;
2145 * This OSC stuff is EVIL. It takes just one character to get into
2146 * sysline mode and it's not initially obvious how to get out.
2147 * So I've added CR and LF as string aborts.
2148 * This shouldn't effect compatibility as I believe embedded
2149 * control characters are supposed to be interpreted (maybe?)
2150 * and they don't display anything useful anyway.
2154 if (c
== '\n' || c
== '\r') {
2155 termstate
= TOPLEVEL
;
2156 } else if (c
== 0234 || c
== '\007') {
2158 * These characters terminate the string; ST and BEL
2159 * terminate the sequence and trigger instant
2160 * processing of it, whereas ESC goes back to SEEN_ESC
2161 * mode unless it is followed by \, in which case it is
2162 * synonymous with ST in the first place.
2165 termstate
= TOPLEVEL
;
2166 } else if (c
== '\033')
2167 termstate
= OSC_MAYBE_ST
;
2168 else if (osc_strlen
< OSC_STR_MAX
)
2169 osc_string
[osc_strlen
++] = c
;
2173 int max
= (osc_strlen
== 0 ?
21 : 16);
2175 if (c
>= '0' && c
<= '9')
2177 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2179 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2182 termstate
= TOPLEVEL
;
2185 osc_string
[osc_strlen
++] = val
;
2186 if (osc_strlen
>= 7) {
2187 palette_set(osc_string
[0],
2188 osc_string
[1] * 16 + osc_string
[2],
2189 osc_string
[3] * 16 + osc_string
[4],
2190 osc_string
[5] * 16 + osc_string
[6]);
2192 termstate
= TOPLEVEL
;
2208 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2211 termstate
= OSC_STRING
;
2216 termstate
= TOPLEVEL
;
2217 seen_disp_event
= TRUE
;
2220 move(curs
.x
, curs
.y
- 1, 1);
2223 move(curs
.x
, curs
.y
+ 1, 1);
2226 move(curs
.x
+ 1, curs
.y
, 1);
2229 move(curs
.x
- 1, curs
.y
, 1);
2232 * From the VT100 Manual
2233 * NOTE: The special graphics characters in the VT100
2234 * are different from those in the VT52
2236 * From VT102 manual:
2237 * 137 _ Blank - Same
2238 * 140 ` Reserved - Humm.
2239 * 141 a Solid rectangle - Similar
2240 * 142 b 1/ - Top half of fraction for the
2241 * 143 c 3/ - subscript numbers below.
2244 * 146 f Degrees - Same
2245 * 147 g Plus or minus - Same
2247 * 151 i Ellipsis (dots)
2250 * 154 l Bar at scan 0
2251 * 155 m Bar at scan 1
2252 * 156 n Bar at scan 2
2253 * 157 o Bar at scan 3 - Similar
2254 * 160 p Bar at scan 4 - Similar
2255 * 161 q Bar at scan 5 - Similar
2256 * 162 r Bar at scan 6 - Same
2257 * 163 s Bar at scan 7 - Similar
2272 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2275 cset_attr
[cset
= 0] = ATTR_ASCII
;
2282 scroll(0, rows
- 1, -1, TRUE
);
2283 else if (curs
.y
> 0)
2289 erase_lots(FALSE
, FALSE
, TRUE
);
2293 erase_lots(TRUE
, FALSE
, TRUE
);
2297 /* XXX Print cursor line */
2300 /* XXX Start controller mode */
2303 /* XXX Stop controller mode */
2307 termstate
= VT52_Y1
;
2310 ldisc_send("\033/Z", 3);
2313 app_keypad_keys
= TRUE
;
2316 app_keypad_keys
= FALSE
;
2319 /* XXX This should switch to VT100 mode not current or default
2320 * VT mode. But this will only have effect in a VT220+
2324 blink_is_real
= cfg
.blinktext
;
2328 /* XXX Enter auto print mode */
2331 /* XXX Exit auto print mode */
2334 /* XXX Print screen */
2340 /* compatibility(ATARI) */
2342 erase_lots(FALSE
, FALSE
, TRUE
);
2346 /* compatibility(ATARI) */
2347 if (curs
.y
<= marg_b
)
2348 scroll(curs
.y
, marg_b
, -1, FALSE
);
2351 /* compatibility(ATARI) */
2352 if (curs
.y
<= marg_b
)
2353 scroll(curs
.y
, marg_b
, 1, TRUE
);
2356 /* compatibility(ATARI) */
2357 termstate
= VT52_FG
;
2360 /* compatibility(ATARI) */
2361 termstate
= VT52_BG
;
2364 /* compatibility(ATARI) */
2365 erase_lots(FALSE
, TRUE
, FALSE
);
2369 /* compatibility(ATARI) */
2373 /* compatibility(ATARI) */
2376 /* case 'j': Save cursor position - broken on ST */
2377 /* case 'k': Restore cursor position */
2379 /* compatibility(ATARI) */
2380 erase_lots(TRUE
, TRUE
, TRUE
);
2386 /* compatibility(ATARI) */
2387 erase_lots(TRUE
, TRUE
, FALSE
);
2390 /* compatibility(ATARI) */
2391 curr_attr
|= ATTR_REVERSE
;
2394 /* compatibility(ATARI) */
2395 curr_attr
&= ~ATTR_REVERSE
;
2397 case 'v': /* wrap Autowrap on - Wyse style */
2398 /* compatibility(ATARI) */
2401 case 'w': /* Autowrap off */
2402 /* compatibility(ATARI) */
2407 /* compatibility(OTHER) */
2409 curr_attr
= ATTR_DEFAULT
;
2411 erase_char
= (' ' | ATTR_ASCII
|
2413 (ATTR_FGMASK
| ATTR_BGMASK
)));
2416 /* compatibility(VI50) */
2417 curr_attr
|= ATTR_UNDER
;
2420 /* compatibility(VI50) */
2421 curr_attr
&= ~ATTR_UNDER
;
2424 /* compatibility(VI50) */
2426 curr_attr
|= ATTR_BOLD
;
2429 /* compatibility(VI50) */
2431 curr_attr
&= ~ATTR_BOLD
;
2437 termstate
= VT52_Y2
;
2438 move(curs
.x
, c
- ' ', 0);
2441 termstate
= TOPLEVEL
;
2442 move(c
- ' ', curs
.y
, 0);
2447 termstate
= TOPLEVEL
;
2448 curr_attr
&= ~ATTR_FGMASK
;
2449 curr_attr
&= ~ATTR_BOLD
;
2450 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2451 if ((c
& 0x8) || vt52_bold
)
2452 curr_attr
|= ATTR_BOLD
;
2455 erase_char
= (' ' | ATTR_ASCII
|
2456 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2459 termstate
= TOPLEVEL
;
2460 curr_attr
&= ~ATTR_BGMASK
;
2461 curr_attr
&= ~ATTR_BLINK
;
2462 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2464 /* Note: bold background */
2466 curr_attr
|= ATTR_BLINK
;
2469 erase_char
= (' ' | ATTR_ASCII
|
2470 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2473 default: break; /* placate gcc warning about enum use */
2475 if (selstate
!= NO_SELECTION
) {
2476 pos cursplus
= curs
;
2478 check_selection(curs
, cursplus
);
2486 * Compare two lines to determine whether they are sufficiently
2487 * alike to scroll-optimise one to the other. Return the degree of
2490 static int linecmp(unsigned long *a
, unsigned long *b
)
2494 for (i
= n
= 0; i
< cols
; i
++)
2495 n
+= (*a
++ == *b
++);
2501 * Given a context, update the window. Out of paranoia, we don't
2502 * allow WM_PAINT responses to do scrolling optimisations.
2504 static void do_paint(Context ctx
, int may_optimise
)
2506 int i
, j
, our_curs_y
;
2507 unsigned long rv
, cursor
;
2510 long cursor_background
= ERASE_CHAR
;
2514 * Check the visual bell state.
2517 ticks
= GetTickCount();
2518 if (ticks
- vbell_timeout
>= 0)
2522 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2525 * screen array, disptop, scrtop,
2527 * cfg.blinkpc, blink_is_real, tblinker,
2528 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2531 /* Has the cursor position or type changed ? */
2534 if (blinker
|| !cfg
.blink_cur
)
2535 cursor
= TATTR_ACTCURS
;
2539 cursor
= TATTR_PASCURS
;
2541 cursor
|= TATTR_RIGHTCURS
;
2544 our_curs_y
= curs
.y
- disptop
;
2546 if (dispcurs
&& (curstype
!= cursor
||
2548 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2549 if (dispcurs
> disptext
&&
2550 (*dispcurs
& (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2551 dispcurs
[-1] |= ATTR_INVALID
;
2552 if ( (dispcurs
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2553 dispcurs
[1] |= ATTR_INVALID
;
2554 *dispcurs
|= ATTR_INVALID
;
2559 /* The normal screen data */
2560 for (i
= 0; i
< rows
; i
++) {
2561 unsigned long *ldata
;
2563 int idx
, dirty_line
, dirty_run
;
2564 unsigned long attr
= 0;
2565 int updated_line
= 0;
2568 int last_run_dirty
= 0;
2570 scrpos
.y
= i
+ disptop
;
2571 ldata
= lineptr(scrpos
.y
);
2572 lattr
= (ldata
[cols
] & LATTR_MODE
);
2574 idx
= i
* (cols
+ 1);
2575 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2576 disptext
[idx
+ cols
] = ldata
[cols
];
2578 for (j
= 0; j
< cols
; j
++, idx
++) {
2579 unsigned long tattr
, tchar
;
2580 unsigned long *d
= ldata
+ j
;
2584 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2585 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2586 switch (tchar
& CSET_MASK
) {
2588 tchar
= unitab_line
[tchar
& 0xFF];
2591 tchar
= unitab_xterm
[tchar
& 0xFF];
2594 tchar
= unitab_scoacs
[tchar
&0xFF];
2597 tattr
|= (tchar
& CSET_MASK
);
2599 if ((d
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2602 /* Video reversing things */
2604 ^ (posle(selstart
, scrpos
) &&
2605 poslt(scrpos
, selend
) ? ATTR_REVERSE
: 0));
2607 /* 'Real' blinking ? */
2608 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2609 if (has_focus
&& tblinker
) {
2611 tattr
&= ~CSET_MASK
;
2614 tattr
&= ~ATTR_BLINK
;
2618 * Check the font we'll _probably_ be using to see if
2619 * the character is wide when we don't want it to be.
2621 if ((tchar
| tattr
) != (disptext
[idx
]& ~ATTR_NARROW
)) {
2622 if ((tattr
& ATTR_WIDE
) == 0 &&
2623 CharWidth(ctx
, (tchar
| tattr
) & 0xFFFF) == 2)
2624 tattr
|= ATTR_NARROW
;
2625 } else if (disptext
[idx
]&ATTR_NARROW
)
2626 tattr
|= ATTR_NARROW
;
2628 /* Cursor here ? Save the 'background' */
2629 if (i
== our_curs_y
&& j
== curs
.x
) {
2630 cursor_background
= tattr
| tchar
;
2631 dispcurs
= disptext
+ idx
;
2634 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2637 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2639 /* Special hack for VT100 Linedraw glyphs */
2640 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2641 && tchar
<= 0xBD) break_run
= TRUE
;
2643 if (!dbcs_screenfont
&& !dirty_line
) {
2644 if ((tchar
| tattr
) == disptext
[idx
])
2646 else if (!dirty_run
&& ccount
== 1)
2651 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2652 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2658 if (dbcs_screenfont
)
2659 last_run_dirty
= dirty_run
;
2660 dirty_run
= dirty_line
;
2663 if ((tchar
| tattr
) != disptext
[idx
])
2665 ch
[ccount
++] = (char) tchar
;
2666 disptext
[idx
] = tchar
| tattr
;
2668 /* If it's a wide char step along to the next one. */
2669 if (tattr
& ATTR_WIDE
) {
2673 /* Cursor is here ? Ouch! */
2674 if (i
== our_curs_y
&& j
== curs
.x
) {
2675 cursor_background
= *d
;
2676 dispcurs
= disptext
+ idx
;
2678 if (disptext
[idx
] != *d
)
2684 if (dirty_run
&& ccount
> 0) {
2685 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2689 /* Cursor on this line ? (and changed) */
2690 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2691 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2692 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2693 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2700 * Flick the switch that says if blinking things should be shown or hidden.
2703 void term_blink(int flg
)
2705 static long last_blink
= 0;
2706 static long last_tblink
= 0;
2707 long now
, blink_diff
;
2709 now
= GetTickCount();
2710 blink_diff
= now
- last_tblink
;
2712 /* Make sure the text blinks no more than 2Hz */
2713 if (blink_diff
< 0 || blink_diff
> 450) {
2715 tblinker
= !tblinker
;
2724 blink_diff
= now
- last_blink
;
2726 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2727 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2735 * Invalidate the whole screen so it will be repainted in full.
2737 void term_invalidate(void)
2741 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2742 disptext
[i
] = ATTR_INVALID
;
2746 * Paint the window in response to a WM_PAINT message.
2748 void term_paint(Context ctx
, int left
, int top
, int right
, int bottom
)
2751 if (left
< 0) left
= 0;
2752 if (top
< 0) top
= 0;
2753 if (right
>= cols
) right
= cols
-1;
2754 if (bottom
>= rows
) bottom
= rows
-1;
2756 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2757 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2758 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2759 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2761 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2762 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2765 /* This should happen soon enough, also for some reason it sometimes
2766 * fails to actually do anything when re-sizing ... painting the wrong
2770 do_paint (ctx
, FALSE
);
2774 * Attempt to scroll the scrollback. The second parameter gives the
2775 * position we want to scroll to; the first is +1 to denote that
2776 * this position is relative to the beginning of the scrollback, -1
2777 * to denote it is relative to the end, and 0 to denote that it is
2778 * relative to the current position.
2780 void term_scroll(int rel
, int where
)
2782 int sbtop
= -count234(scrollback
);
2784 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2785 if (disptop
< sbtop
)
2793 static void clipme(pos top
, pos bottom
)
2796 wchar_t *wbptr
; /* where next char goes within workbuf */
2797 int wblen
= 0; /* workbuf len */
2798 int buflen
; /* amount of memory allocated to workbuf */
2800 buflen
= 5120; /* Default size */
2801 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2802 wbptr
= workbuf
; /* start filling here */
2804 while (poslt(top
, bottom
)) {
2806 unsigned long *ldata
= lineptr(top
.y
);
2812 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2813 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2814 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2815 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2816 && poslt(top
, nlpos
))
2818 if (poslt(nlpos
, bottom
))
2821 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2824 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2826 wchar_t cbuf
[16], *p
;
2827 int uc
= (ldata
[top
.x
] & 0xFFFF);
2830 if (uc
== UCSWIDE
) {
2835 switch (uc
& CSET_MASK
) {
2838 uc
= unitab_xterm
[uc
& 0xFF];
2842 uc
= unitab_line
[uc
& 0xFF];
2845 uc
= unitab_scoacs
[uc
&0xFF];
2848 switch (uc
& CSET_MASK
) {
2850 uc
= unitab_font
[uc
& 0xFF];
2853 uc
= unitab_oemcp
[uc
& 0xFF];
2857 set
= (uc
& CSET_MASK
);
2858 c
= (uc
& CHAR_MASK
);
2862 if (DIRECT_FONT(uc
)) {
2863 if (c
>= ' ' && c
!= 0x7F) {
2864 unsigned char buf
[4];
2867 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
2869 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
2870 rv
= MultiByteToWideChar(font_codepage
,
2871 0, buf
, 2, wbuf
, 4);
2875 rv
= MultiByteToWideChar(font_codepage
,
2876 0, buf
, 1, wbuf
, 4);
2880 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
2887 for (p
= cbuf
; *p
; p
++) {
2888 /* Enough overhead for trailing NL and nul */
2889 if (wblen
>= buflen
- 16) {
2892 sizeof(wchar_t) * (buflen
+= 100));
2893 wbptr
= workbuf
+ wblen
;
2902 for (i
= 0; i
< sel_nl_sz
; i
++) {
2904 *wbptr
++ = sel_nl
[i
];
2912 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
2913 if (buflen
> 0) /* indicates we allocated this buffer */
2917 void term_copyall(void)
2920 top
.y
= -count234(scrollback
);
2926 * The wordness array is mainly for deciding the disposition of the US-ASCII
2929 static int wordtype(int uc
)
2932 int start
, end
, ctype
;
2933 } *wptr
, ucs_words
[] = {
2939 0x037e, 0x037e, 1}, /* Greek question mark */
2941 0x0387, 0x0387, 1}, /* Greek ano teleia */
2943 0x055a, 0x055f, 1}, /* Armenian punctuation */
2945 0x0589, 0x0589, 1}, /* Armenian full stop */
2947 0x0700, 0x070d, 1}, /* Syriac punctuation */
2949 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2951 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2953 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2955 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2957 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2959 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2961 0x2000, 0x200a, 0}, /* Various spaces */
2963 0x2070, 0x207f, 2}, /* superscript */
2965 0x2080, 0x208f, 2}, /* subscript */
2967 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2969 0x3000, 0x3000, 0}, /* ideographic space */
2971 0x3001, 0x3020, 1}, /* ideographic punctuation */
2973 0x303f, 0x309f, 3}, /* Hiragana */
2975 0x30a0, 0x30ff, 3}, /* Katakana */
2977 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2979 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2981 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2983 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2985 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2987 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2989 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2991 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2993 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2998 uc
&= (CSET_MASK
| CHAR_MASK
);
3000 switch (uc
& CSET_MASK
) {
3002 uc
= unitab_xterm
[uc
& 0xFF];
3005 uc
= unitab_line
[uc
& 0xFF];
3008 uc
= unitab_scoacs
[uc
&0xFF];
3011 switch (uc
& CSET_MASK
) {
3013 uc
= unitab_font
[uc
& 0xFF];
3016 uc
= unitab_oemcp
[uc
& 0xFF];
3020 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3021 * fail as there's such a thing as a double width space. :-(
3023 if (dbcs_screenfont
&& font_codepage
== line_codepage
)
3027 return wordness
[uc
];
3029 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
3030 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
3038 * Spread the selection outwards according to the selection mode.
3040 static pos
sel_spread_half(pos p
, int dir
)
3042 unsigned long *ldata
;
3045 ldata
= lineptr(p
.y
);
3050 * In this mode, every character is a separate unit, except
3051 * for runs of spaces at the end of a non-wrapping line.
3053 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
3054 unsigned long *q
= ldata
+ cols
;
3055 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
3057 if (q
== ldata
+ cols
)
3059 if (p
.x
>= q
- ldata
)
3060 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
3065 * In this mode, the units are maximal runs of characters
3066 * whose `wordness' has the same value.
3068 wvalue
= wordtype(ldata
[p
.x
]);
3070 while (p
.x
< cols
&& wordtype(ldata
[p
.x
+ 1]) == wvalue
)
3073 while (p
.x
> 0 && wordtype(ldata
[p
.x
- 1]) == wvalue
)
3079 * In this mode, every line is a unit.
3081 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3087 static void sel_spread(void)
3089 selstart
= sel_spread_half(selstart
, -1);
3091 selend
= sel_spread_half(selend
, +1);
3095 void term_do_paste(void)
3100 get_clip(&data
, &len
);
3105 sfree(paste_buffer
);
3106 paste_pos
= paste_hold
= paste_len
= 0;
3107 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3110 while (p
< data
+ len
) {
3111 while (p
< data
+ len
&&
3112 !(p
<= data
+ len
- sel_nl_sz
&&
3113 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3118 for (i
= 0; i
< p
- q
; i
++) {
3119 paste_buffer
[paste_len
++] = q
[i
];
3123 if (p
<= data
+ len
- sel_nl_sz
&&
3124 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3125 paste_buffer
[paste_len
++] = '\r';
3131 /* Assume a small paste will be OK in one go. */
3132 if (paste_len
< 256) {
3133 luni_send(paste_buffer
, paste_len
);
3135 sfree(paste_buffer
);
3137 paste_pos
= paste_hold
= paste_len
= 0;
3140 get_clip(NULL
, NULL
);
3143 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3144 int shift
, int ctrl
)
3147 unsigned long *ldata
;
3148 int raw_mouse
= xterm_mouse
&& !(cfg
.mouse_override
&& shift
);
3152 if (a
== MA_DRAG
&& !raw_mouse
)
3157 if (a
== MA_DRAG
&& !raw_mouse
)
3170 selpoint
.y
= y
+ disptop
;
3172 ldata
= lineptr(selpoint
.y
);
3173 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3177 int encstate
= 0, r
, c
;
3179 static int is_down
= 0;
3183 encstate
= 0x20; /* left button down */
3194 case MBT_WHEEL_DOWN
:
3197 default: break; /* placate gcc warning about enum use */
3201 if (xterm_mouse
== 1)
3214 default: break; /* placate gcc warning about enum use */
3223 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3224 ldisc_send(abuf
, 6);
3228 b
= translate_button(b
);
3230 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3232 selstate
= ABOUT_TO
;
3233 selanchor
= selpoint
;
3235 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3237 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3238 selstate
= DRAGGING
;
3239 selstart
= selanchor
= selpoint
;
3243 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3244 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3245 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3247 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3248 if (posdiff(selpoint
, selstart
) <
3249 posdiff(selend
, selstart
) / 2) {
3253 selanchor
= selstart
;
3255 selstate
= DRAGGING
;
3257 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3258 selanchor
= selpoint
;
3259 selstate
= DRAGGING
;
3260 if (poslt(selpoint
, selanchor
)) {
3261 selstart
= selpoint
;
3265 selstart
= selanchor
;
3270 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3271 if (selstate
== DRAGGING
) {
3273 * We've completed a selection. We now transfer the
3274 * data to the clipboard.
3276 clipme(selstart
, selend
);
3277 selstate
= SELECTED
;
3279 selstate
= NO_SELECTION
;
3280 } else if (b
== MBT_PASTE
3281 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3292 sfree(paste_buffer
);
3299 static long last_paste
= 0;
3300 long now
, paste_diff
;
3305 /* Don't wait forever to paste */
3307 now
= GetTickCount();
3308 paste_diff
= now
- last_paste
;
3309 if (paste_diff
>= 0 && paste_diff
< 450)
3314 while (paste_pos
< paste_len
) {
3316 while (n
+ paste_pos
< paste_len
) {
3317 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3320 luni_send(paste_buffer
+ paste_pos
, n
);
3323 if (paste_pos
< paste_len
) {
3328 sfree(paste_buffer
);
3333 static void deselect(void)
3335 selstate
= NO_SELECTION
;
3336 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3339 void term_deselect(void)
3345 int term_ldisc(int option
)
3347 if (option
== LD_ECHO
)
3348 return term_echoing
;
3349 if (option
== LD_EDIT
)
3350 return term_editing
;
3355 * from_backend(), to get data from the backend for the terminal.
3357 int from_backend(int is_stderr
, char *data
, int len
)
3360 if (inbuf_head
>= INBUF_SIZE
)
3362 inbuf
[inbuf_head
++] = *data
++;
3366 * We process all stdout/stderr data immediately we receive it,
3367 * and don't return until it's all gone. Therefore, there's no
3368 * reason at all to return anything other than zero from this
3371 * This is a slightly suboptimal way to deal with SSH2 - in
3372 * principle, the window mechanism would allow us to continue
3373 * to accept data on forwarded ports and X connections even
3374 * while the terminal processing was going slowly - but we
3375 * can't do the 100% right thing without moving the terminal
3376 * processing into a separate thread, and that might hurt
3377 * portability. So we manage stdout buffering the old SSH1 way:
3378 * if the terminal processing goes slowly, the whole SSH
3379 * connection stops accepting data until it's ready.
3381 * In practice, I can't imagine this causing serious trouble.
3387 * Log session traffic.
3389 void logtraffic(unsigned char c
, int logmode
)
3391 if (cfg
.logtype
> 0) {
3392 if (cfg
.logtype
== logmode
) {
3393 /* deferred open file from pgm start? */
3402 void settimstr(char *ta
, int no_sec
);
3403 char *subslfcode(char *dest
, char *src
, char *dstrt
);
3404 char *stpncpy(char *dst
, const char *src
, size_t maxlen
);
3406 char currlogfilename
[FILENAME_MAX
];
3408 /* open log file append/overwrite mode */
3418 sprintf(writemod
, "wb"); /* default to rewrite */
3421 tm
= *localtime(&t
);
3423 /* substitute special codes in file name */
3424 xlatlognam(currlogfilename
,cfg
.logfilename
,cfg
.host
, &tm
);
3426 lgfp
= fopen(currlogfilename
, "r"); /* file already present? */
3430 i
= askappend(currlogfilename
);
3432 writemod
[0] = 'a'; /* set append mode */
3433 else if (i
== 0) { /* cancelled */
3435 cfg
.logtype
= 0; /* disable logging */
3440 lgfp
= fopen(currlogfilename
, writemod
);
3441 if (lgfp
) { /* enter into event log */
3442 sprintf(buf
, "%s session log (%s mode) to file : ",
3443 (writemod
[0] == 'a') ?
"Appending" : "Writing new",
3444 (cfg
.logtype
== LGTYP_ASCII ?
"ASCII" :
3445 cfg
.logtype
== LGTYP_DEBUG ?
"raw" : "<ukwn>"));
3446 /* Make sure we do not exceed the output buffer size */
3447 strncat(buf
, currlogfilename
, 128);
3448 buf
[strlen(buf
)] = '\0';
3451 /* --- write header line into log file */
3452 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp
);
3453 strftime(buf
, 24, "%Y.%m.%d %H:%M:%S", &tm
);
3455 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp
);
3459 void logfclose(void)
3468 * translate format codes into time/date strings
3469 * and insert them into log file name
3471 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3473 static void xlatlognam(char *d
, char *s
, char *hostname
, struct tm
*tm
) {
3474 char buf
[10], *bufp
;
3476 char *ds
= d
; /* save start pos. */
3477 int len
= FILENAME_MAX
-1;
3480 /* Let (bufp, len) be the string to append. */
3481 bufp
= buf
; /* don't usually override this */
3485 if (*s
) switch (c
= *s
++, tolower(c
)) {
3487 size
= strftime(buf
, sizeof(buf
), "%Y", tm
);
3490 size
= strftime(buf
, sizeof(buf
), "%m", tm
);
3493 size
= strftime(buf
, sizeof(buf
), "%d", tm
);
3496 size
= strftime(buf
, sizeof(buf
), "%H%M%S", tm
);
3500 size
= strlen(bufp
);
3514 memcpy(d
, bufp
, size
);