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)
322 int need_sbar_update
= seen_disp_event
;
323 if ((seen_key_event
&& (cfg
.scroll_on_key
)) ||
324 (seen_disp_event
&& (cfg
.scroll_on_disp
))) {
325 disptop
= 0; /* return to main screen */
326 seen_disp_event
= seen_key_event
= 0;
327 need_sbar_update
= TRUE
;
329 if (need_sbar_update
)
332 sys_cursor(curs
.x
, curs
.y
- disptop
);
338 * Same as power_on(), but an external function.
340 void term_pwron(void)
350 * Clear the scrollback.
352 void term_clrsb(void)
356 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
363 * Initialise the terminal.
367 screen
= alt_screen
= scrollback
= NULL
;
369 disptext
= dispcurs
= NULL
;
374 beephead
= beeptail
= NULL
;
377 beep_overloaded
= FALSE
;
381 * Set up the terminal for a given size.
383 void term_size(int newrows
, int newcols
, int newsavelines
)
386 unsigned long *newdisp
, *line
;
389 int save_alt_which
= alt_which
;
391 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
392 return; /* nothing to do */
398 alt_b
= marg_b
= newrows
- 1;
401 scrollback
= newtree234(NULL
);
402 screen
= newtree234(NULL
);
407 * Resize the screen and scrollback. We only need to shift
408 * lines around within our data structures, because lineptr()
409 * will take care of resizing each individual line if
412 * - If the new screen and the old screen differ in length, we
413 * must shunt some lines in from the scrollback or out to
416 * - If doing that fails to provide us with enough material to
417 * fill the new screen (i.e. the number of rows needed in
418 * the new screen exceeds the total number in the previous
419 * screen+scrollback), we must invent some blank lines to
422 * - Then, if the new scrollback length is less than the
423 * amount of scrollback we actually have, we must throw some
426 sblen
= count234(scrollback
);
427 /* Do this loop to expand the screen if newrows > rows */
428 for (i
= rows
; i
< newrows
; i
++) {
430 line
= delpos234(scrollback
, --sblen
);
432 line
= smalloc(TSIZE
* (newcols
+ 2));
434 for (j
= 0; j
<= newcols
; j
++)
435 line
[j
+ 1] = ERASE_CHAR
;
437 addpos234(screen
, line
, 0);
439 /* Do this loop to shrink the screen if newrows < rows */
440 for (i
= newrows
; i
< rows
; i
++) {
441 line
= delpos234(screen
, 0);
442 addpos234(scrollback
, line
, sblen
++);
444 assert(count234(screen
) == newrows
);
445 while (sblen
> newsavelines
) {
446 line
= delpos234(scrollback
, 0);
450 assert(count234(scrollback
) <= newsavelines
);
453 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
454 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
455 newdisp
[i
] = ATTR_INVALID
;
460 newalt
= newtree234(NULL
);
461 for (i
= 0; i
< newrows
; i
++) {
462 line
= smalloc(TSIZE
* (newcols
+ 2));
464 for (j
= 0; j
<= newcols
; j
++)
465 line
[j
+ 1] = erase_char
;
466 addpos234(newalt
, line
, i
);
469 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
471 freetree234(alt_screen
);
475 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
478 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
479 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
483 curs
.y
+= newrows
- rows
;
486 if (curs
.y
>= newrows
)
487 curs
.y
= newrows
- 1;
488 if (curs
.x
>= newcols
)
489 curs
.x
= newcols
- 1;
491 wrapnext
= alt_wnext
= FALSE
;
495 savelines
= newsavelines
;
498 swap_screen(save_alt_which
);
508 static void swap_screen(int which
)
513 if (which
== alt_which
)
540 wrapnext
= alt_wnext
;
552 sco_acs
= alt_sco_acs
;
559 * Update the scroll bar.
561 static void update_sbar(void)
565 nscroll
= count234(scrollback
);
567 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
571 * Check whether the region bounded by the two pointers intersects
572 * the scroll region, and de-select the on-screen selection if so.
574 static void check_selection(pos from
, pos to
)
576 if (poslt(from
, selend
) && poslt(selstart
, to
))
581 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
582 * for backward.) `sb' is TRUE if the scrolling is permitted to
583 * affect the scrollback buffer.
585 * NB this function invalidates all pointers into lines of the
586 * screen data structures. In particular, you MUST call fix_cpos
587 * after calling scroll() and before doing anything else that
588 * uses the cpos shortcut pointer.
590 static void scroll(int topline
, int botline
, int lines
, int sb
)
592 unsigned long *line
, *line2
;
595 if (topline
!= 0 || alt_which
!= 0)
600 line
= delpos234(screen
, botline
);
601 line
= resizeline(line
, cols
);
602 for (i
= 0; i
< cols
; i
++)
603 line
[i
+ 1] = erase_char
;
605 addpos234(screen
, line
, topline
);
607 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
609 if (selstart
.y
> botline
) {
610 selstart
.y
= botline
;
614 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
616 if (selend
.y
> botline
) {
626 line
= delpos234(screen
, topline
);
627 if (sb
&& savelines
> 0) {
628 int sblen
= count234(scrollback
);
630 * We must add this line to the scrollback. We'll
631 * remove a line from the top of the scrollback to
632 * replace it, or allocate a new one if the
633 * scrollback isn't full.
635 if (sblen
== savelines
) {
636 sblen
--, line2
= delpos234(scrollback
, 0);
638 line2
= smalloc(TSIZE
* (cols
+ 2));
641 addpos234(scrollback
, line
, sblen
);
645 * If the user is currently looking at part of the
646 * scrollback, and they haven't enabled any options
647 * that are going to reset the scrollback as a
648 * result of this movement, then the chances are
649 * they'd like to keep looking at the same line. So
650 * we move their viewpoint at the same rate as the
651 * scroll, at least until their viewpoint hits the
652 * top end of the scrollback buffer, at which point
653 * we don't have the choice any more.
655 * Thanks to Jan Holmen Holsten for the idea and
656 * initial implementation.
658 if (disptop
> -savelines
&& disptop
< 0)
661 line
= resizeline(line
, cols
);
662 for (i
= 0; i
< cols
; i
++)
663 line
[i
+ 1] = erase_char
;
665 addpos234(screen
, line
, botline
);
668 * If the selection endpoints move into the scrollback,
669 * we keep them moving until they hit the top. However,
670 * of course, if the line _hasn't_ moved into the
671 * scrollback then we don't do this, and cut them off
672 * at the top of the scroll region.
674 * This applies to selstart and selend (for an existing
675 * selection), and also selanchor (for one being
676 * selected as we speak).
678 seltop
= sb ?
-savelines
: 0;
680 if (selstart
.y
>= seltop
&& selstart
.y
<= botline
) {
682 if (selstart
.y
< seltop
) {
687 if (selend
.y
>= seltop
&& selend
.y
<= botline
) {
689 if (selend
.y
< seltop
) {
694 if (selanchor
.y
>= seltop
&& selanchor
.y
<= botline
) {
696 if (selanchor
.y
< seltop
) {
697 selanchor
.y
= seltop
;
708 * Move the cursor to a given position, clipping at boundaries. We
709 * may or may not want to clip at the scroll margin: marg_clip is 0
710 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
711 * even _being_ outside the margins.
713 static void move(int x
, int y
, int marg_clip
)
720 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
722 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
736 * Save or restore the cursor and SGR mode.
738 static void save_cursor(int save
)
742 save_attr
= curr_attr
;
745 save_csattr
= cset_attr
[cset
];
746 save_sco_acs
= sco_acs
;
749 /* Make sure the window hasn't shrunk since the save */
755 curr_attr
= save_attr
;
758 cset_attr
[cset
] = save_csattr
;
759 sco_acs
= save_sco_acs
;
762 erase_char
= (' ' | ATTR_ASCII
|
763 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
768 * Erase a large portion of the screen: the whole screen, or the
769 * whole line, or parts thereof.
771 static void erase_lots(int line_only
, int from_begin
, int to_end
)
775 unsigned long *ldata
;
797 check_selection(start
, end
);
799 /* Clear screen also forces a full window redraw, just in case. */
800 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
803 ldata
= lineptr(start
.y
);
804 while (poslt(start
, end
)) {
805 if (start
.x
== cols
&& !erase_lattr
)
806 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
808 ldata
[start
.x
] = erase_char
;
809 if (incpos(start
) && start
.y
< rows
)
810 ldata
= lineptr(start
.y
);
815 * Insert or delete characters within the current line. n is +ve if
816 * insertion is desired, and -ve for deletion.
818 static void insch(int n
)
820 int dir
= (n
< 0 ?
-1 : +1);
823 unsigned long *ldata
;
825 n
= (n
< 0 ?
-n
: n
);
826 if (n
> cols
- curs
.x
)
828 m
= cols
- curs
.x
- n
;
830 cursplus
.x
= curs
.x
+ n
;
831 check_selection(curs
, cursplus
);
832 ldata
= lineptr(curs
.y
);
834 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
836 ldata
[curs
.x
+ m
++] = erase_char
;
838 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
840 ldata
[curs
.x
+ n
] = erase_char
;
845 * Toggle terminal mode `mode' to state `state'. (`query' indicates
846 * whether the mode is a DEC private one or a normal one.)
848 static void toggle_mode(int mode
, int query
, int state
)
854 case 1: /* application cursor keys */
855 app_cursor_keys
= state
;
857 case 2: /* VT52 mode */
860 blink_is_real
= FALSE
;
863 blink_is_real
= cfg
.blinktext
;
866 case 3: /* 80/132 columns */
868 request_resize(state ?
132 : 80, rows
);
871 case 5: /* reverse video */
873 * Toggle reverse video. If we receive an OFF within the
874 * visual bell timeout period after an ON, we trigger an
875 * effective visual bell, so that ESC[?5hESC[?5l will
876 * always be an actually _visible_ visual bell.
878 ticks
= GetTickCount();
879 if (rvideo
&& !state
&& /* we're turning it off */
880 ticks
< rvbell_timeout
) { /* and it's not long since it was turned on */
881 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
882 if (vbell_timeout
< rvbell_timeout
) /* don't move vbell end forward */
883 vbell_timeout
= rvbell_timeout
; /* vbell end is at least then */
884 } else if (!rvideo
&& state
) {
885 /* This is an ON, so we notice the time and save it. */
886 rvbell_timeout
= ticks
+ VBELL_TIMEOUT
;
889 seen_disp_event
= TRUE
;
893 case 6: /* DEC origin mode */
896 case 7: /* auto wrap */
899 case 8: /* auto key repeat */
902 case 10: /* set local edit mode */
903 term_editing
= state
;
904 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
906 case 25: /* enable/disable cursor */
907 compatibility2(OTHER
, VT220
);
909 seen_disp_event
= TRUE
;
911 case 47: /* alternate screen */
912 compatibility(OTHER
);
917 case 1000: /* xterm mouse 1 */
918 xterm_mouse
= state ?
1 : 0;
919 set_raw_mouse_mode(state
);
921 case 1002: /* xterm mouse 2 */
922 xterm_mouse
= state ?
2 : 0;
923 set_raw_mouse_mode(state
);
927 case 4: /* set insert mode */
928 compatibility(VT102
);
931 case 12: /* set echo mode */
932 term_echoing
= !state
;
933 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
935 case 20: /* Return sends ... */
936 cr_lf_return
= state
;
938 case 34: /* Make cursor BIG */
939 compatibility2(OTHER
, VT220
);
945 * Process an OSC sequence: set window title or icon name.
947 static void do_osc(void)
951 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
953 osc_string
[osc_strlen
] = '\0';
954 switch (esc_args
[0]) {
957 set_icon(osc_string
);
958 if (esc_args
[0] == 1)
960 /* fall through: parameter 0 means set both */
963 set_title(osc_string
);
970 * Remove everything currently in `inbuf' and stick it up on the
971 * in-memory display. There's a big state machine in here to
972 * process escape sequences...
979 * Optionally log the session traffic to a file. Useful for
980 * debugging and possibly also useful for actual logging.
982 if (cfg
.logtype
== LGTYP_DEBUG
)
983 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
984 logtraffic((unsigned char) inbuf
[inbuf_reap
], LGTYP_DEBUG
);
987 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
988 c
= inbuf
[inbuf_reap
];
990 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
991 * be able to display 8-bit characters, but I'll let that go 'cause
995 /* First see about all those translations. */
996 if (termstate
== TOPLEVEL
) {
1001 /* UTF-8 must be stateless so we ignore iso2022. */
1002 if (unitab_ctrl
[c
] != 0xFF)
1004 else c
= ((unsigned char)c
) | ATTR_ASCII
;
1006 } else if ((c
& 0xe0) == 0xc0) {
1007 utf_size
= utf_state
= 1;
1008 utf_char
= (c
& 0x1f);
1009 } else if ((c
& 0xf0) == 0xe0) {
1010 utf_size
= utf_state
= 2;
1011 utf_char
= (c
& 0x0f);
1012 } else if ((c
& 0xf8) == 0xf0) {
1013 utf_size
= utf_state
= 3;
1014 utf_char
= (c
& 0x07);
1015 } else if ((c
& 0xfc) == 0xf8) {
1016 utf_size
= utf_state
= 4;
1017 utf_char
= (c
& 0x03);
1018 } else if ((c
& 0xfe) == 0xfc) {
1019 utf_size
= utf_state
= 5;
1020 utf_char
= (c
& 0x01);
1031 if ((c
& 0xC0) != 0x80) {
1037 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
1043 /* Is somebody trying to be evil! */
1045 (c
< 0x800 && utf_size
>= 2) ||
1046 (c
< 0x10000 && utf_size
>= 3) ||
1047 (c
< 0x200000 && utf_size
>= 4) ||
1048 (c
< 0x4000000 && utf_size
>= 5))
1051 /* Unicode line separator and paragraph separator are CR-LF */
1052 if (c
== 0x2028 || c
== 0x2029)
1055 /* High controls are probably a Baaad idea too. */
1059 /* The UTF-16 surrogates are not nice either. */
1060 /* The standard give the option of decoding these:
1061 * I don't want to! */
1062 if (c
>= 0xD800 && c
< 0xE000)
1065 /* ISO 10646 characters now limited to UTF-16 range. */
1069 /* This is currently a TagPhobic application.. */
1070 if (c
>= 0xE0000 && c
<= 0xE007F)
1073 /* U+FEFF is best seen as a null. */
1076 /* But U+FFFE is an error. */
1077 if (c
== 0xFFFE || c
== 0xFFFF)
1080 /* Oops this is a 16bit implementation */
1085 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1087 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1089 if (sco_acs
== 2) c
^= 0x80;
1092 switch (cset_attr
[cset
]) {
1094 * Linedraw characters are different from 'ESC ( B'
1095 * only for a small range. For ones outside that
1096 * range, make sure we use the same font as well as
1097 * the same encoding.
1100 if (unitab_ctrl
[c
] != 0xFF)
1103 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1107 /* If UK-ASCII, make the '#' a LineDraw Pound */
1109 c
= '}' | ATTR_LINEDRW
;
1112 /*FALLTHROUGH*/ case ATTR_ASCII
:
1113 if (unitab_ctrl
[c
] != 0xFF)
1116 c
= ((unsigned char) c
) | ATTR_ASCII
;
1119 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1125 /* How about C1 controls ? */
1126 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1127 has_compat(VT220
)) {
1128 termstate
= SEEN_ESC
;
1130 c
= '@' + (c
& 0x1F);
1133 /* Or the GL control. */
1134 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1135 if (curs
.x
&& !wrapnext
)
1139 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1141 /* Or normal C0 controls. */
1142 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1144 case '\005': /* terminal type query */
1145 /* Strictly speaking this is VT100 but a VT100 defaults to
1146 * no response. Other terminals respond at their option.
1148 * Don't put a CR in the default string as this tends to
1149 * upset some weird software.
1151 * An xterm returns "xterm" (5 characters)
1153 compatibility(ANSIMIN
);
1155 char abuf
[256], *s
, *d
;
1157 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1159 if (*s
>= 'a' && *s
<= 'z')
1160 *d
++ = (*s
- ('a' - 1));
1161 else if ((*s
>= '@' && *s
<= '_') ||
1162 *s
== '?' || (*s
& 0x80))
1167 } else if (*s
== '^') {
1172 lpage_send(CP_ACP
, abuf
, d
- abuf
);
1177 struct beeptime
*newbeep
;
1180 ticks
= GetTickCount();
1182 if (!beep_overloaded
) {
1183 newbeep
= smalloc(sizeof(struct beeptime
));
1184 newbeep
->ticks
= ticks
;
1185 newbeep
->next
= NULL
;
1189 beeptail
->next
= newbeep
;
1195 * Throw out any beeps that happened more than
1199 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1200 struct beeptime
*tmp
= beephead
;
1201 beephead
= tmp
->next
;
1208 if (cfg
.bellovl
&& beep_overloaded
&&
1209 ticks
- lastbeep
>= cfg
.bellovl_s
) {
1211 * If we're currently overloaded and the
1212 * last beep was more than s seconds ago,
1213 * leave overload mode.
1215 beep_overloaded
= FALSE
;
1216 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1217 nbeeps
>= cfg
.bellovl_n
) {
1219 * Now, if we have n or more beeps
1220 * remaining in the queue, go into overload
1223 beep_overloaded
= TRUE
;
1228 * Perform an actual beep if we're not overloaded.
1230 if (!cfg
.bellovl
|| !beep_overloaded
) {
1232 if (cfg
.beep
== BELL_VISUAL
) {
1234 vbell_timeout
= ticks
+ VBELL_TIMEOUT
;
1242 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1243 else if (curs
.x
== 0 && curs
.y
> 0)
1244 curs
.x
= cols
- 1, curs
.y
--;
1250 seen_disp_event
= TRUE
;
1253 compatibility(VT100
);
1257 compatibility(VT100
);
1262 termstate
= VT52_ESC
;
1264 compatibility(ANSIMIN
);
1265 termstate
= SEEN_ESC
;
1273 seen_disp_event
= TRUE
;
1275 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1278 if (has_compat(SCOANSI
)) {
1280 erase_lots(FALSE
, FALSE
, TRUE
);
1283 seen_disp_event
= 1;
1287 compatibility(VT100
);
1289 if (curs
.y
== marg_b
)
1290 scroll(marg_t
, marg_b
, 1, TRUE
);
1291 else if (curs
.y
< rows
- 1)
1297 seen_disp_event
= 1;
1299 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1303 pos old_curs
= curs
;
1304 unsigned long *ldata
= lineptr(curs
.y
);
1308 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1310 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1311 if (curs
.x
>= cols
/ 2)
1312 curs
.x
= cols
/ 2 - 1;
1319 check_selection(old_curs
, curs
);
1321 seen_disp_event
= TRUE
;
1325 switch (termstate
) {
1327 /* Only graphic characters get this far, ctrls are stripped above */
1328 if (wrapnext
&& wrap
) {
1329 cpos
[1] |= LATTR_WRAPPED
;
1330 if (curs
.y
== marg_b
)
1331 scroll(marg_t
, marg_b
, 1, TRUE
);
1332 else if (curs
.y
< rows
- 1)
1340 if (selstate
!= NO_SELECTION
) {
1341 pos cursplus
= curs
;
1343 check_selection(curs
, cursplus
);
1345 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1346 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1348 extern int wcwidth(wchar_t ucs
);
1353 width
= wcwidth((wchar_t) c
);
1356 *cpos
++ = c
| curr_attr
;
1357 if (++curs
.x
== cols
) {
1358 *cpos
|= LATTR_WRAPPED
;
1359 if (curs
.y
== marg_b
)
1360 scroll(marg_t
, marg_b
, 1, TRUE
);
1361 else if (curs
.y
< rows
- 1)
1366 *cpos
++ = UCSWIDE
| curr_attr
;
1369 *cpos
++ = c
| curr_attr
;
1376 if (curs
.x
== cols
) {
1380 if (wrap
&& vt52_mode
) {
1381 cpos
[1] |= LATTR_WRAPPED
;
1382 if (curs
.y
== marg_b
)
1383 scroll(marg_t
, marg_b
, 1, TRUE
);
1384 else if (curs
.y
< rows
- 1)
1391 seen_disp_event
= 1;
1396 * This state is virtually identical to SEEN_ESC, with the
1397 * exception that we have an OSC sequence in the pipeline,
1398 * and _if_ we see a backslash, we process it.
1402 termstate
= TOPLEVEL
;
1405 /* else fall through */
1407 if (c
>= ' ' && c
<= '/') {
1414 termstate
= TOPLEVEL
;
1415 switch (ANSI(c
, esc_query
)) {
1416 case '[': /* enter CSI mode */
1417 termstate
= SEEN_CSI
;
1419 esc_args
[0] = ARG_DEFAULT
;
1422 case ']': /* xterm escape sequences */
1423 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1424 compatibility(OTHER
);
1425 termstate
= SEEN_OSC
;
1428 case '7': /* save cursor */
1429 compatibility(VT100
);
1432 case '8': /* restore cursor */
1433 compatibility(VT100
);
1435 seen_disp_event
= TRUE
;
1438 compatibility(VT100
);
1439 app_keypad_keys
= TRUE
;
1442 compatibility(VT100
);
1443 app_keypad_keys
= FALSE
;
1445 case 'D': /* exactly equivalent to LF */
1446 compatibility(VT100
);
1447 if (curs
.y
== marg_b
)
1448 scroll(marg_t
, marg_b
, 1, TRUE
);
1449 else if (curs
.y
< rows
- 1)
1453 seen_disp_event
= TRUE
;
1455 case 'E': /* exactly equivalent to CR-LF */
1456 compatibility(VT100
);
1458 if (curs
.y
== marg_b
)
1459 scroll(marg_t
, marg_b
, 1, TRUE
);
1460 else if (curs
.y
< rows
- 1)
1464 seen_disp_event
= TRUE
;
1466 case 'M': /* reverse index - backwards LF */
1467 compatibility(VT100
);
1468 if (curs
.y
== marg_t
)
1469 scroll(marg_t
, marg_b
, -1, TRUE
);
1470 else if (curs
.y
> 0)
1474 seen_disp_event
= TRUE
;
1476 case 'Z': /* terminal type query */
1477 compatibility(VT100
);
1478 ldisc_send(id_string
, strlen(id_string
));
1480 case 'c': /* restore power-on settings */
1481 compatibility(VT100
);
1484 request_resize(80, rows
);
1489 seen_disp_event
= TRUE
;
1491 case 'H': /* set a tab */
1492 compatibility(VT100
);
1493 tabs
[curs
.x
] = TRUE
;
1496 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1497 compatibility(VT100
);
1499 unsigned long *ldata
;
1503 for (i
= 0; i
< rows
; i
++) {
1505 for (j
= 0; j
< cols
; j
++)
1506 ldata
[j
] = ATTR_DEFAULT
| 'E';
1510 seen_disp_event
= TRUE
;
1511 scrtop
.x
= scrtop
.y
= 0;
1514 check_selection(scrtop
, scrbot
);
1518 case ANSI('3', '#'):
1519 case ANSI('4', '#'):
1520 case ANSI('5', '#'):
1521 case ANSI('6', '#'):
1522 compatibility(VT100
);
1524 unsigned long nlattr
;
1525 unsigned long *ldata
;
1526 switch (ANSI(c
, esc_query
)) {
1527 case ANSI('3', '#'):
1530 case ANSI('4', '#'):
1533 case ANSI('5', '#'):
1534 nlattr
= LATTR_NORM
;
1536 default: /* spiritually case ANSI('6', '#'): */
1537 nlattr
= LATTR_WIDE
;
1540 ldata
= lineptr(curs
.y
);
1541 ldata
[cols
] &= ~LATTR_MODE
;
1542 ldata
[cols
] |= nlattr
;
1546 case ANSI('A', '('):
1547 compatibility(VT100
);
1548 cset_attr
[0] = ATTR_GBCHR
;
1550 case ANSI('B', '('):
1551 compatibility(VT100
);
1552 cset_attr
[0] = ATTR_ASCII
;
1554 case ANSI('0', '('):
1555 compatibility(VT100
);
1556 cset_attr
[0] = ATTR_LINEDRW
;
1558 case ANSI('U', '('):
1559 compatibility(OTHER
);
1560 cset_attr
[0] = ATTR_SCOACS
;
1563 case ANSI('A', ')'):
1564 compatibility(VT100
);
1565 cset_attr
[1] = ATTR_GBCHR
;
1567 case ANSI('B', ')'):
1568 compatibility(VT100
);
1569 cset_attr
[1] = ATTR_ASCII
;
1571 case ANSI('0', ')'):
1572 compatibility(VT100
);
1573 cset_attr
[1] = ATTR_LINEDRW
;
1575 case ANSI('U', ')'):
1576 compatibility(OTHER
);
1577 cset_attr
[1] = ATTR_SCOACS
;
1580 case ANSI('8', '%'): /* Old Linux code */
1581 case ANSI('G', '%'):
1582 compatibility(OTHER
);
1585 case ANSI('@', '%'):
1586 compatibility(OTHER
);
1592 termstate
= TOPLEVEL
; /* default */
1594 if (esc_nargs
<= ARGS_MAX
) {
1595 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1596 esc_args
[esc_nargs
- 1] = 0;
1597 esc_args
[esc_nargs
- 1] =
1598 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1600 termstate
= SEEN_CSI
;
1601 } else if (c
== ';') {
1602 if (++esc_nargs
<= ARGS_MAX
)
1603 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1604 termstate
= SEEN_CSI
;
1605 } else if (c
< '@') {
1612 termstate
= SEEN_CSI
;
1614 switch (ANSI(c
, esc_query
)) {
1615 case 'A': /* move up N lines */
1616 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1617 seen_disp_event
= TRUE
;
1619 case 'e': /* move down N lines */
1620 compatibility(ANSI
);
1623 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1624 seen_disp_event
= TRUE
;
1626 case ANSI('c', '>'): /* report xterm version */
1627 compatibility(OTHER
);
1628 /* this reports xterm version 136 so that VIM can
1629 use the drag messages from the mouse reporting */
1630 ldisc_send("\033[>0;136;0c", 11);
1632 case 'a': /* move right N cols */
1633 compatibility(ANSI
);
1636 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1637 seen_disp_event
= TRUE
;
1639 case 'D': /* move left N cols */
1640 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1641 seen_disp_event
= TRUE
;
1643 case 'E': /* move down N lines and CR */
1644 compatibility(ANSI
);
1645 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1646 seen_disp_event
= TRUE
;
1648 case 'F': /* move up N lines and CR */
1649 compatibility(ANSI
);
1650 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1651 seen_disp_event
= TRUE
;
1654 case '`': /* set horizontal posn */
1655 compatibility(ANSI
);
1656 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1657 seen_disp_event
= TRUE
;
1659 case 'd': /* set vertical posn */
1660 compatibility(ANSI
);
1662 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1665 seen_disp_event
= TRUE
;
1668 case 'f': /* set horz and vert posns at once */
1670 esc_args
[1] = ARG_DEFAULT
;
1671 move(def(esc_args
[1], 1) - 1,
1672 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1675 seen_disp_event
= TRUE
;
1677 case 'J': /* erase screen or parts of it */
1679 unsigned int i
= def(esc_args
[0], 0) + 1;
1682 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1685 seen_disp_event
= TRUE
;
1687 case 'K': /* erase line or parts of it */
1689 unsigned int i
= def(esc_args
[0], 0) + 1;
1692 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1694 seen_disp_event
= TRUE
;
1696 case 'L': /* insert lines */
1697 compatibility(VT102
);
1698 if (curs
.y
<= marg_b
)
1699 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1702 seen_disp_event
= TRUE
;
1704 case 'M': /* delete lines */
1705 compatibility(VT102
);
1706 if (curs
.y
<= marg_b
)
1707 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1710 seen_disp_event
= TRUE
;
1712 case '@': /* insert chars */
1713 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1714 compatibility(VT102
);
1715 insch(def(esc_args
[0], 1));
1716 seen_disp_event
= TRUE
;
1718 case 'P': /* delete chars */
1719 compatibility(VT102
);
1720 insch(-def(esc_args
[0], 1));
1721 seen_disp_event
= TRUE
;
1723 case 'c': /* terminal type query */
1724 compatibility(VT100
);
1725 /* This is the response for a VT102 */
1726 ldisc_send(id_string
, strlen(id_string
));
1728 case 'n': /* cursor position query */
1729 if (esc_args
[0] == 6) {
1731 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1733 ldisc_send(buf
, strlen(buf
));
1734 } else if (esc_args
[0] == 5) {
1735 ldisc_send("\033[0n", 4);
1738 case 'h': /* toggle modes to high */
1740 compatibility(VT100
);
1743 for (i
= 0; i
< esc_nargs
; i
++)
1744 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1747 case 'l': /* toggle modes to low */
1749 compatibility(VT100
);
1752 for (i
= 0; i
< esc_nargs
; i
++)
1753 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1756 case 'g': /* clear tabs */
1757 compatibility(VT100
);
1758 if (esc_nargs
== 1) {
1759 if (esc_args
[0] == 0) {
1760 tabs
[curs
.x
] = FALSE
;
1761 } else if (esc_args
[0] == 3) {
1763 for (i
= 0; i
< cols
; i
++)
1768 case 'r': /* set scroll margins */
1769 compatibility(VT100
);
1770 if (esc_nargs
<= 2) {
1772 top
= def(esc_args
[0], 1) - 1;
1773 bot
= (esc_nargs
<= 1
1775 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1778 /* VTTEST Bug 9 - if region is less than 2 lines
1779 * don't change region.
1781 if (bot
- top
> 0) {
1786 * I used to think the cursor should be
1787 * placed at the top of the newly marginned
1788 * area. Apparently not: VMS TPU falls over
1791 * Well actually it should for Origin mode - RDB
1793 curs
.y
= (dec_om ? marg_t
: 0);
1795 seen_disp_event
= TRUE
;
1799 case 'm': /* set graphics rendition */
1802 * A VT100 without the AVO only had one attribute, either
1803 * underline or reverse video depending on the cursor type,
1804 * this was selected by CSI 7m.
1807 * This is sometimes DIM, eg on the GIGI and Linux
1809 * This is sometimes INVIS various ANSI.
1811 * This like 22 disables BOLD, DIM and INVIS
1813 * The ANSI colours appear on any terminal that has colour
1814 * (obviously) but the interaction between sgr0 and the
1815 * colours varies but is usually related to the background
1816 * colour erase item.
1817 * The interaction between colour attributes and the mono
1818 * ones is also very implementation dependent.
1820 * The 39 and 49 attributes are likely to be unimplemented.
1823 for (i
= 0; i
< esc_nargs
; i
++) {
1824 switch (def(esc_args
[i
], 0)) {
1825 case 0: /* restore defaults */
1826 curr_attr
= ATTR_DEFAULT
;
1828 case 1: /* enable bold */
1829 compatibility(VT100AVO
);
1830 curr_attr
|= ATTR_BOLD
;
1832 case 21: /* (enable double underline) */
1833 compatibility(OTHER
);
1834 case 4: /* enable underline */
1835 compatibility(VT100AVO
);
1836 curr_attr
|= ATTR_UNDER
;
1838 case 5: /* enable blink */
1839 compatibility(VT100AVO
);
1840 curr_attr
|= ATTR_BLINK
;
1842 case 7: /* enable reverse video */
1843 curr_attr
|= ATTR_REVERSE
;
1845 case 10: /* SCO acs off */
1846 compatibility(SCOANSI
);
1848 case 11: /* SCO acs on */
1849 compatibility(SCOANSI
);
1851 case 12: /* SCO acs on flipped */
1852 compatibility(SCOANSI
);
1854 case 22: /* disable bold */
1855 compatibility2(OTHER
, VT220
);
1856 curr_attr
&= ~ATTR_BOLD
;
1858 case 24: /* disable underline */
1859 compatibility2(OTHER
, VT220
);
1860 curr_attr
&= ~ATTR_UNDER
;
1862 case 25: /* disable blink */
1863 compatibility2(OTHER
, VT220
);
1864 curr_attr
&= ~ATTR_BLINK
;
1866 case 27: /* disable reverse video */
1867 compatibility2(OTHER
, VT220
);
1868 curr_attr
&= ~ATTR_REVERSE
;
1879 curr_attr
&= ~ATTR_FGMASK
;
1881 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1883 case 39: /* default-foreground */
1884 curr_attr
&= ~ATTR_FGMASK
;
1885 curr_attr
|= ATTR_DEFFG
;
1896 curr_attr
&= ~ATTR_BGMASK
;
1898 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1900 case 49: /* default-background */
1901 curr_attr
&= ~ATTR_BGMASK
;
1902 curr_attr
|= ATTR_DEFBG
;
1907 erase_char
= (' ' | ATTR_ASCII
|
1909 (ATTR_FGMASK
| ATTR_BGMASK
)));
1912 case 's': /* save cursor */
1915 case 'u': /* restore cursor */
1917 seen_disp_event
= TRUE
;
1919 case 't': /* set page size - ie window height */
1921 * VT340/VT420 sequence DECSLPP, DEC only allows values
1922 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1923 * illegal values (eg first arg 1..9) for window changing
1926 compatibility(VT340TEXT
);
1928 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1929 request_resize(cols
, def(esc_args
[0], 24));
1934 compatibility(SCOANSI
);
1935 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
1938 seen_disp_event
= TRUE
;
1941 compatibility(SCOANSI
);
1942 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
1945 seen_disp_event
= TRUE
;
1947 case ANSI('|', '*'):
1948 /* VT420 sequence DECSNLS
1949 * Set number of lines on screen
1950 * VT420 uses VGA like hardware and can support any size in
1951 * reasonable range (24..49 AIUI) with no default specified.
1953 compatibility(VT420
);
1954 if (esc_nargs
== 1 && esc_args
[0] > 0) {
1955 request_resize(cols
, def(esc_args
[0], cfg
.height
));
1959 case ANSI('|', '$'):
1960 /* VT340/VT420 sequence DECSCPP
1961 * Set number of columns per page
1962 * Docs imply range is only 80 or 132, but I'll allow any.
1964 compatibility(VT340TEXT
);
1965 if (esc_nargs
<= 1) {
1966 request_resize(def(esc_args
[0], cfg
.width
), rows
);
1970 case 'X': /* write N spaces w/o moving cursor */
1971 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1972 compatibility(ANSIMIN
);
1974 int n
= def(esc_args
[0], 1);
1976 unsigned long *p
= cpos
;
1977 if (n
> cols
- curs
.x
)
1981 check_selection(curs
, cursplus
);
1984 seen_disp_event
= TRUE
;
1987 case 'x': /* report terminal characteristics */
1988 compatibility(VT100
);
1991 int i
= def(esc_args
[0], 0);
1992 if (i
== 0 || i
== 1) {
1993 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
1995 ldisc_send(buf
, 20);
1999 case 'Z': /* BackTab for xterm */
2000 compatibility(OTHER
);
2002 int i
= def(esc_args
[0], 1);
2003 pos old_curs
= curs
;
2005 for(;i
>0 && curs
.x
>0; i
--) {
2008 } while (curs
.x
>0 && !tabs
[curs
.x
]);
2011 check_selection(old_curs
, curs
);
2014 case ANSI('L', '='):
2015 compatibility(OTHER
);
2016 use_bce
= (esc_args
[0] <= 0);
2017 erase_char
= ERASE_CHAR
;
2019 erase_char
= (' ' | ATTR_ASCII
|
2021 (ATTR_FGMASK
| ATTR_BGMASK
)));
2023 case ANSI('E', '='):
2024 compatibility(OTHER
);
2025 blink_is_real
= (esc_args
[0] >= 1);
2027 case ANSI('p', '"'):
2028 /* Allow the host to make this emulator a 'perfect' VT102.
2029 * This first appeared in the VT220, but we do need to get
2030 * back to PuTTY mode so I won't check it.
2032 * The arg in 40..42,50 are a PuTTY extension.
2033 * The 2nd arg, 8bit vs 7bit is not checked.
2035 * Setting VT102 mode should also change the Fkeys to
2036 * generate PF* codes as a real VT102 has no Fkeys.
2037 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2040 * Note ESC c will NOT change this!
2043 switch (esc_args
[0]) {
2045 compatibility_level
&= ~TM_VTXXX
;
2046 compatibility_level
|= TM_VT102
;
2049 compatibility_level
&= ~TM_VTXXX
;
2050 compatibility_level
|= TM_VT220
;
2054 if (esc_args
[0] > 60 && esc_args
[0] < 70)
2055 compatibility_level
|= TM_VTXXX
;
2059 compatibility_level
&= TM_VTXXX
;
2062 compatibility_level
= TM_PUTTY
;
2065 compatibility_level
= TM_SCOANSI
;
2069 compatibility_level
= TM_PUTTY
;
2075 /* Change the response to CSI c */
2076 if (esc_args
[0] == 50) {
2079 strcpy(id_string
, "\033[?");
2080 for (i
= 1; i
< esc_nargs
; i
++) {
2082 strcat(id_string
, ";");
2083 sprintf(lbuf
, "%d", esc_args
[i
]);
2084 strcat(id_string
, lbuf
);
2086 strcat(id_string
, "c");
2089 /* Is this a good idea ?
2090 * Well we should do a soft reset at this point ...
2092 if (!has_compat(VT420
) && has_compat(VT100
)) {
2094 request_resize(132, 24);
2096 request_resize(80, 24);
2105 case 'P': /* Linux palette sequence */
2106 termstate
= SEEN_OSC_P
;
2109 case 'R': /* Linux palette reset */
2112 termstate
= TOPLEVEL
;
2114 case 'W': /* word-set */
2115 termstate
= SEEN_OSC_W
;
2128 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2132 * Grotty hack to support xterm and DECterm title
2133 * sequences concurrently.
2135 if (esc_args
[0] == 2) {
2139 /* else fall through */
2141 termstate
= OSC_STRING
;
2147 * This OSC stuff is EVIL. It takes just one character to get into
2148 * sysline mode and it's not initially obvious how to get out.
2149 * So I've added CR and LF as string aborts.
2150 * This shouldn't effect compatibility as I believe embedded
2151 * control characters are supposed to be interpreted (maybe?)
2152 * and they don't display anything useful anyway.
2156 if (c
== '\n' || c
== '\r') {
2157 termstate
= TOPLEVEL
;
2158 } else if (c
== 0234 || c
== '\007') {
2160 * These characters terminate the string; ST and BEL
2161 * terminate the sequence and trigger instant
2162 * processing of it, whereas ESC goes back to SEEN_ESC
2163 * mode unless it is followed by \, in which case it is
2164 * synonymous with ST in the first place.
2167 termstate
= TOPLEVEL
;
2168 } else if (c
== '\033')
2169 termstate
= OSC_MAYBE_ST
;
2170 else if (osc_strlen
< OSC_STR_MAX
)
2171 osc_string
[osc_strlen
++] = c
;
2175 int max
= (osc_strlen
== 0 ?
21 : 16);
2177 if (c
>= '0' && c
<= '9')
2179 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2181 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2184 termstate
= TOPLEVEL
;
2187 osc_string
[osc_strlen
++] = val
;
2188 if (osc_strlen
>= 7) {
2189 palette_set(osc_string
[0],
2190 osc_string
[1] * 16 + osc_string
[2],
2191 osc_string
[3] * 16 + osc_string
[4],
2192 osc_string
[5] * 16 + osc_string
[6]);
2194 termstate
= TOPLEVEL
;
2210 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2213 termstate
= OSC_STRING
;
2218 termstate
= TOPLEVEL
;
2219 seen_disp_event
= TRUE
;
2222 move(curs
.x
, curs
.y
- 1, 1);
2225 move(curs
.x
, curs
.y
+ 1, 1);
2228 move(curs
.x
+ 1, curs
.y
, 1);
2231 move(curs
.x
- 1, curs
.y
, 1);
2234 * From the VT100 Manual
2235 * NOTE: The special graphics characters in the VT100
2236 * are different from those in the VT52
2238 * From VT102 manual:
2239 * 137 _ Blank - Same
2240 * 140 ` Reserved - Humm.
2241 * 141 a Solid rectangle - Similar
2242 * 142 b 1/ - Top half of fraction for the
2243 * 143 c 3/ - subscript numbers below.
2246 * 146 f Degrees - Same
2247 * 147 g Plus or minus - Same
2249 * 151 i Ellipsis (dots)
2252 * 154 l Bar at scan 0
2253 * 155 m Bar at scan 1
2254 * 156 n Bar at scan 2
2255 * 157 o Bar at scan 3 - Similar
2256 * 160 p Bar at scan 4 - Similar
2257 * 161 q Bar at scan 5 - Similar
2258 * 162 r Bar at scan 6 - Same
2259 * 163 s Bar at scan 7 - Similar
2274 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2277 cset_attr
[cset
= 0] = ATTR_ASCII
;
2284 scroll(0, rows
- 1, -1, TRUE
);
2285 else if (curs
.y
> 0)
2291 erase_lots(FALSE
, FALSE
, TRUE
);
2295 erase_lots(TRUE
, FALSE
, TRUE
);
2299 /* XXX Print cursor line */
2302 /* XXX Start controller mode */
2305 /* XXX Stop controller mode */
2309 termstate
= VT52_Y1
;
2312 ldisc_send("\033/Z", 3);
2315 app_keypad_keys
= TRUE
;
2318 app_keypad_keys
= FALSE
;
2321 /* XXX This should switch to VT100 mode not current or default
2322 * VT mode. But this will only have effect in a VT220+
2326 blink_is_real
= cfg
.blinktext
;
2330 /* XXX Enter auto print mode */
2333 /* XXX Exit auto print mode */
2336 /* XXX Print screen */
2342 /* compatibility(ATARI) */
2344 erase_lots(FALSE
, FALSE
, TRUE
);
2348 /* compatibility(ATARI) */
2349 if (curs
.y
<= marg_b
)
2350 scroll(curs
.y
, marg_b
, -1, FALSE
);
2353 /* compatibility(ATARI) */
2354 if (curs
.y
<= marg_b
)
2355 scroll(curs
.y
, marg_b
, 1, TRUE
);
2358 /* compatibility(ATARI) */
2359 termstate
= VT52_FG
;
2362 /* compatibility(ATARI) */
2363 termstate
= VT52_BG
;
2366 /* compatibility(ATARI) */
2367 erase_lots(FALSE
, TRUE
, FALSE
);
2371 /* compatibility(ATARI) */
2375 /* compatibility(ATARI) */
2378 /* case 'j': Save cursor position - broken on ST */
2379 /* case 'k': Restore cursor position */
2381 /* compatibility(ATARI) */
2382 erase_lots(TRUE
, TRUE
, TRUE
);
2388 /* compatibility(ATARI) */
2389 erase_lots(TRUE
, TRUE
, FALSE
);
2392 /* compatibility(ATARI) */
2393 curr_attr
|= ATTR_REVERSE
;
2396 /* compatibility(ATARI) */
2397 curr_attr
&= ~ATTR_REVERSE
;
2399 case 'v': /* wrap Autowrap on - Wyse style */
2400 /* compatibility(ATARI) */
2403 case 'w': /* Autowrap off */
2404 /* compatibility(ATARI) */
2409 /* compatibility(OTHER) */
2411 curr_attr
= ATTR_DEFAULT
;
2413 erase_char
= (' ' | ATTR_ASCII
|
2415 (ATTR_FGMASK
| ATTR_BGMASK
)));
2418 /* compatibility(VI50) */
2419 curr_attr
|= ATTR_UNDER
;
2422 /* compatibility(VI50) */
2423 curr_attr
&= ~ATTR_UNDER
;
2426 /* compatibility(VI50) */
2428 curr_attr
|= ATTR_BOLD
;
2431 /* compatibility(VI50) */
2433 curr_attr
&= ~ATTR_BOLD
;
2439 termstate
= VT52_Y2
;
2440 move(curs
.x
, c
- ' ', 0);
2443 termstate
= TOPLEVEL
;
2444 move(c
- ' ', curs
.y
, 0);
2449 termstate
= TOPLEVEL
;
2450 curr_attr
&= ~ATTR_FGMASK
;
2451 curr_attr
&= ~ATTR_BOLD
;
2452 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2453 if ((c
& 0x8) || vt52_bold
)
2454 curr_attr
|= ATTR_BOLD
;
2457 erase_char
= (' ' | ATTR_ASCII
|
2458 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2461 termstate
= TOPLEVEL
;
2462 curr_attr
&= ~ATTR_BGMASK
;
2463 curr_attr
&= ~ATTR_BLINK
;
2464 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2466 /* Note: bold background */
2468 curr_attr
|= ATTR_BLINK
;
2471 erase_char
= (' ' | ATTR_ASCII
|
2472 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2475 default: break; /* placate gcc warning about enum use */
2477 if (selstate
!= NO_SELECTION
) {
2478 pos cursplus
= curs
;
2480 check_selection(curs
, cursplus
);
2488 * Compare two lines to determine whether they are sufficiently
2489 * alike to scroll-optimise one to the other. Return the degree of
2492 static int linecmp(unsigned long *a
, unsigned long *b
)
2496 for (i
= n
= 0; i
< cols
; i
++)
2497 n
+= (*a
++ == *b
++);
2503 * Given a context, update the window. Out of paranoia, we don't
2504 * allow WM_PAINT responses to do scrolling optimisations.
2506 static void do_paint(Context ctx
, int may_optimise
)
2508 int i
, j
, our_curs_y
;
2509 unsigned long rv
, cursor
;
2512 long cursor_background
= ERASE_CHAR
;
2516 * Check the visual bell state.
2519 ticks
= GetTickCount();
2520 if (ticks
- vbell_timeout
>= 0)
2524 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2527 * screen array, disptop, scrtop,
2529 * cfg.blinkpc, blink_is_real, tblinker,
2530 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2533 /* Has the cursor position or type changed ? */
2536 if (blinker
|| !cfg
.blink_cur
)
2537 cursor
= TATTR_ACTCURS
;
2541 cursor
= TATTR_PASCURS
;
2543 cursor
|= TATTR_RIGHTCURS
;
2546 our_curs_y
= curs
.y
- disptop
;
2548 if (dispcurs
&& (curstype
!= cursor
||
2550 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2551 if (dispcurs
> disptext
&&
2552 (*dispcurs
& (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2553 dispcurs
[-1] |= ATTR_INVALID
;
2554 if ( (dispcurs
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2555 dispcurs
[1] |= ATTR_INVALID
;
2556 *dispcurs
|= ATTR_INVALID
;
2561 /* The normal screen data */
2562 for (i
= 0; i
< rows
; i
++) {
2563 unsigned long *ldata
;
2565 int idx
, dirty_line
, dirty_run
;
2566 unsigned long attr
= 0;
2567 int updated_line
= 0;
2570 int last_run_dirty
= 0;
2572 scrpos
.y
= i
+ disptop
;
2573 ldata
= lineptr(scrpos
.y
);
2574 lattr
= (ldata
[cols
] & LATTR_MODE
);
2576 idx
= i
* (cols
+ 1);
2577 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2578 disptext
[idx
+ cols
] = ldata
[cols
];
2580 for (j
= 0; j
< cols
; j
++, idx
++) {
2581 unsigned long tattr
, tchar
;
2582 unsigned long *d
= ldata
+ j
;
2586 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2587 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2588 switch (tchar
& CSET_MASK
) {
2590 tchar
= unitab_line
[tchar
& 0xFF];
2593 tchar
= unitab_xterm
[tchar
& 0xFF];
2596 tchar
= unitab_scoacs
[tchar
&0xFF];
2599 tattr
|= (tchar
& CSET_MASK
);
2601 if ((d
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2604 /* Video reversing things */
2606 ^ (posle(selstart
, scrpos
) &&
2607 poslt(scrpos
, selend
) ? ATTR_REVERSE
: 0));
2609 /* 'Real' blinking ? */
2610 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2611 if (has_focus
&& tblinker
) {
2613 tattr
&= ~CSET_MASK
;
2616 tattr
&= ~ATTR_BLINK
;
2620 * Check the font we'll _probably_ be using to see if
2621 * the character is wide when we don't want it to be.
2623 if ((tchar
| tattr
) != (disptext
[idx
]& ~ATTR_NARROW
)) {
2624 if ((tattr
& ATTR_WIDE
) == 0 &&
2625 CharWidth(ctx
, (tchar
| tattr
) & 0xFFFF) == 2)
2626 tattr
|= ATTR_NARROW
;
2627 } else if (disptext
[idx
]&ATTR_NARROW
)
2628 tattr
|= ATTR_NARROW
;
2630 /* Cursor here ? Save the 'background' */
2631 if (i
== our_curs_y
&& j
== curs
.x
) {
2632 cursor_background
= tattr
| tchar
;
2633 dispcurs
= disptext
+ idx
;
2636 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2639 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2641 /* Special hack for VT100 Linedraw glyphs */
2642 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2643 && tchar
<= 0xBD) break_run
= TRUE
;
2645 if (!dbcs_screenfont
&& !dirty_line
) {
2646 if ((tchar
| tattr
) == disptext
[idx
])
2648 else if (!dirty_run
&& ccount
== 1)
2653 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2654 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2660 if (dbcs_screenfont
)
2661 last_run_dirty
= dirty_run
;
2662 dirty_run
= dirty_line
;
2665 if ((tchar
| tattr
) != disptext
[idx
])
2667 ch
[ccount
++] = (char) tchar
;
2668 disptext
[idx
] = tchar
| tattr
;
2670 /* If it's a wide char step along to the next one. */
2671 if (tattr
& ATTR_WIDE
) {
2675 /* Cursor is here ? Ouch! */
2676 if (i
== our_curs_y
&& j
== curs
.x
) {
2677 cursor_background
= *d
;
2678 dispcurs
= disptext
+ idx
;
2680 if (disptext
[idx
] != *d
)
2686 if (dirty_run
&& ccount
> 0) {
2687 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2691 /* Cursor on this line ? (and changed) */
2692 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2693 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2694 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2695 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2702 * Flick the switch that says if blinking things should be shown or hidden.
2705 void term_blink(int flg
)
2707 static long last_blink
= 0;
2708 static long last_tblink
= 0;
2709 long now
, blink_diff
;
2711 now
= GetTickCount();
2712 blink_diff
= now
- last_tblink
;
2714 /* Make sure the text blinks no more than 2Hz */
2715 if (blink_diff
< 0 || blink_diff
> 450) {
2717 tblinker
= !tblinker
;
2726 blink_diff
= now
- last_blink
;
2728 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2729 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2737 * Invalidate the whole screen so it will be repainted in full.
2739 void term_invalidate(void)
2743 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2744 disptext
[i
] = ATTR_INVALID
;
2748 * Paint the window in response to a WM_PAINT message.
2750 void term_paint(Context ctx
, int left
, int top
, int right
, int bottom
)
2753 if (left
< 0) left
= 0;
2754 if (top
< 0) top
= 0;
2755 if (right
>= cols
) right
= cols
-1;
2756 if (bottom
>= rows
) bottom
= rows
-1;
2758 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2759 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2760 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2761 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2763 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2764 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2767 /* This should happen soon enough, also for some reason it sometimes
2768 * fails to actually do anything when re-sizing ... painting the wrong
2772 do_paint (ctx
, FALSE
);
2776 * Attempt to scroll the scrollback. The second parameter gives the
2777 * position we want to scroll to; the first is +1 to denote that
2778 * this position is relative to the beginning of the scrollback, -1
2779 * to denote it is relative to the end, and 0 to denote that it is
2780 * relative to the current position.
2782 void term_scroll(int rel
, int where
)
2784 int sbtop
= -count234(scrollback
);
2786 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2787 if (disptop
< sbtop
)
2795 static void clipme(pos top
, pos bottom
)
2798 wchar_t *wbptr
; /* where next char goes within workbuf */
2799 int wblen
= 0; /* workbuf len */
2800 int buflen
; /* amount of memory allocated to workbuf */
2802 buflen
= 5120; /* Default size */
2803 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2804 wbptr
= workbuf
; /* start filling here */
2806 while (poslt(top
, bottom
)) {
2808 unsigned long *ldata
= lineptr(top
.y
);
2814 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2815 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2816 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2817 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2818 && poslt(top
, nlpos
))
2820 if (poslt(nlpos
, bottom
))
2823 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2826 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2828 wchar_t cbuf
[16], *p
;
2829 int uc
= (ldata
[top
.x
] & 0xFFFF);
2832 if (uc
== UCSWIDE
) {
2837 switch (uc
& CSET_MASK
) {
2840 uc
= unitab_xterm
[uc
& 0xFF];
2844 uc
= unitab_line
[uc
& 0xFF];
2847 uc
= unitab_scoacs
[uc
&0xFF];
2850 switch (uc
& CSET_MASK
) {
2852 uc
= unitab_font
[uc
& 0xFF];
2855 uc
= unitab_oemcp
[uc
& 0xFF];
2859 set
= (uc
& CSET_MASK
);
2860 c
= (uc
& CHAR_MASK
);
2864 if (DIRECT_FONT(uc
)) {
2865 if (c
>= ' ' && c
!= 0x7F) {
2866 unsigned char buf
[4];
2869 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
2871 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
2872 rv
= MultiByteToWideChar(font_codepage
,
2873 0, buf
, 2, wbuf
, 4);
2877 rv
= MultiByteToWideChar(font_codepage
,
2878 0, buf
, 1, wbuf
, 4);
2882 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
2889 for (p
= cbuf
; *p
; p
++) {
2890 /* Enough overhead for trailing NL and nul */
2891 if (wblen
>= buflen
- 16) {
2894 sizeof(wchar_t) * (buflen
+= 100));
2895 wbptr
= workbuf
+ wblen
;
2904 for (i
= 0; i
< sel_nl_sz
; i
++) {
2906 *wbptr
++ = sel_nl
[i
];
2914 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
2915 if (buflen
> 0) /* indicates we allocated this buffer */
2919 void term_copyall(void)
2922 top
.y
= -count234(scrollback
);
2928 * The wordness array is mainly for deciding the disposition of the US-ASCII
2931 static int wordtype(int uc
)
2934 int start
, end
, ctype
;
2935 } *wptr
, ucs_words
[] = {
2941 0x037e, 0x037e, 1}, /* Greek question mark */
2943 0x0387, 0x0387, 1}, /* Greek ano teleia */
2945 0x055a, 0x055f, 1}, /* Armenian punctuation */
2947 0x0589, 0x0589, 1}, /* Armenian full stop */
2949 0x0700, 0x070d, 1}, /* Syriac punctuation */
2951 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2953 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2955 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2957 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2959 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2961 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2963 0x2000, 0x200a, 0}, /* Various spaces */
2965 0x2070, 0x207f, 2}, /* superscript */
2967 0x2080, 0x208f, 2}, /* subscript */
2969 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2971 0x3000, 0x3000, 0}, /* ideographic space */
2973 0x3001, 0x3020, 1}, /* ideographic punctuation */
2975 0x303f, 0x309f, 3}, /* Hiragana */
2977 0x30a0, 0x30ff, 3}, /* Katakana */
2979 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2981 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2983 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2985 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2987 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2989 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2991 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2993 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2995 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3000 uc
&= (CSET_MASK
| CHAR_MASK
);
3002 switch (uc
& CSET_MASK
) {
3004 uc
= unitab_xterm
[uc
& 0xFF];
3007 uc
= unitab_line
[uc
& 0xFF];
3010 uc
= unitab_scoacs
[uc
&0xFF];
3013 switch (uc
& CSET_MASK
) {
3015 uc
= unitab_font
[uc
& 0xFF];
3018 uc
= unitab_oemcp
[uc
& 0xFF];
3022 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3023 * fail as there's such a thing as a double width space. :-(
3025 if (dbcs_screenfont
&& font_codepage
== line_codepage
)
3029 return wordness
[uc
];
3031 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
3032 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
3040 * Spread the selection outwards according to the selection mode.
3042 static pos
sel_spread_half(pos p
, int dir
)
3044 unsigned long *ldata
;
3047 ldata
= lineptr(p
.y
);
3052 * In this mode, every character is a separate unit, except
3053 * for runs of spaces at the end of a non-wrapping line.
3055 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
3056 unsigned long *q
= ldata
+ cols
;
3057 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
3059 if (q
== ldata
+ cols
)
3061 if (p
.x
>= q
- ldata
)
3062 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
3067 * In this mode, the units are maximal runs of characters
3068 * whose `wordness' has the same value.
3070 wvalue
= wordtype(ldata
[p
.x
]);
3072 while (p
.x
< cols
&& wordtype(ldata
[p
.x
+ 1]) == wvalue
)
3075 while (p
.x
> 0 && wordtype(ldata
[p
.x
- 1]) == wvalue
)
3081 * In this mode, every line is a unit.
3083 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3089 static void sel_spread(void)
3091 selstart
= sel_spread_half(selstart
, -1);
3093 selend
= sel_spread_half(selend
, +1);
3097 void term_do_paste(void)
3102 get_clip(&data
, &len
);
3107 sfree(paste_buffer
);
3108 paste_pos
= paste_hold
= paste_len
= 0;
3109 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3112 while (p
< data
+ len
) {
3113 while (p
< data
+ len
&&
3114 !(p
<= data
+ len
- sel_nl_sz
&&
3115 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3120 for (i
= 0; i
< p
- q
; i
++) {
3121 paste_buffer
[paste_len
++] = q
[i
];
3125 if (p
<= data
+ len
- sel_nl_sz
&&
3126 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3127 paste_buffer
[paste_len
++] = '\r';
3133 /* Assume a small paste will be OK in one go. */
3134 if (paste_len
< 256) {
3135 luni_send(paste_buffer
, paste_len
);
3137 sfree(paste_buffer
);
3139 paste_pos
= paste_hold
= paste_len
= 0;
3142 get_clip(NULL
, NULL
);
3145 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3146 int shift
, int ctrl
)
3149 unsigned long *ldata
;
3150 int raw_mouse
= xterm_mouse
&& !(cfg
.mouse_override
&& shift
);
3154 if (a
== MA_DRAG
&& !raw_mouse
)
3159 if (a
== MA_DRAG
&& !raw_mouse
)
3172 selpoint
.y
= y
+ disptop
;
3174 ldata
= lineptr(selpoint
.y
);
3175 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3179 int encstate
= 0, r
, c
;
3181 static int is_down
= 0;
3185 encstate
= 0x20; /* left button down */
3196 case MBT_WHEEL_DOWN
:
3199 default: break; /* placate gcc warning about enum use */
3203 if (xterm_mouse
== 1)
3216 default: break; /* placate gcc warning about enum use */
3225 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3226 ldisc_send(abuf
, 6);
3230 b
= translate_button(b
);
3232 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3234 selstate
= ABOUT_TO
;
3235 selanchor
= selpoint
;
3237 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3239 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3240 selstate
= DRAGGING
;
3241 selstart
= selanchor
= selpoint
;
3245 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3246 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3247 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3249 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3250 if (posdiff(selpoint
, selstart
) <
3251 posdiff(selend
, selstart
) / 2) {
3255 selanchor
= selstart
;
3257 selstate
= DRAGGING
;
3259 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3260 selanchor
= selpoint
;
3261 selstate
= DRAGGING
;
3262 if (poslt(selpoint
, selanchor
)) {
3263 selstart
= selpoint
;
3267 selstart
= selanchor
;
3272 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3273 if (selstate
== DRAGGING
) {
3275 * We've completed a selection. We now transfer the
3276 * data to the clipboard.
3278 clipme(selstart
, selend
);
3279 selstate
= SELECTED
;
3281 selstate
= NO_SELECTION
;
3282 } else if (b
== MBT_PASTE
3283 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3294 sfree(paste_buffer
);
3301 static long last_paste
= 0;
3302 long now
, paste_diff
;
3307 /* Don't wait forever to paste */
3309 now
= GetTickCount();
3310 paste_diff
= now
- last_paste
;
3311 if (paste_diff
>= 0 && paste_diff
< 450)
3316 while (paste_pos
< paste_len
) {
3318 while (n
+ paste_pos
< paste_len
) {
3319 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3322 luni_send(paste_buffer
+ paste_pos
, n
);
3325 if (paste_pos
< paste_len
) {
3330 sfree(paste_buffer
);
3335 static void deselect(void)
3337 selstate
= NO_SELECTION
;
3338 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3341 void term_deselect(void)
3347 int term_ldisc(int option
)
3349 if (option
== LD_ECHO
)
3350 return term_echoing
;
3351 if (option
== LD_EDIT
)
3352 return term_editing
;
3357 * from_backend(), to get data from the backend for the terminal.
3359 int from_backend(int is_stderr
, char *data
, int len
)
3362 if (inbuf_head
>= INBUF_SIZE
)
3364 inbuf
[inbuf_head
++] = *data
++;
3368 * We process all stdout/stderr data immediately we receive it,
3369 * and don't return until it's all gone. Therefore, there's no
3370 * reason at all to return anything other than zero from this
3373 * This is a slightly suboptimal way to deal with SSH2 - in
3374 * principle, the window mechanism would allow us to continue
3375 * to accept data on forwarded ports and X connections even
3376 * while the terminal processing was going slowly - but we
3377 * can't do the 100% right thing without moving the terminal
3378 * processing into a separate thread, and that might hurt
3379 * portability. So we manage stdout buffering the old SSH1 way:
3380 * if the terminal processing goes slowly, the whole SSH
3381 * connection stops accepting data until it's ready.
3383 * In practice, I can't imagine this causing serious trouble.
3389 * Log session traffic.
3391 void logtraffic(unsigned char c
, int logmode
)
3393 if (cfg
.logtype
> 0) {
3394 if (cfg
.logtype
== logmode
) {
3395 /* deferred open file from pgm start? */
3404 void settimstr(char *ta
, int no_sec
);
3405 char *subslfcode(char *dest
, char *src
, char *dstrt
);
3406 char *stpncpy(char *dst
, const char *src
, size_t maxlen
);
3408 char currlogfilename
[FILENAME_MAX
];
3410 /* open log file append/overwrite mode */
3420 sprintf(writemod
, "wb"); /* default to rewrite */
3423 tm
= *localtime(&t
);
3425 /* substitute special codes in file name */
3426 xlatlognam(currlogfilename
,cfg
.logfilename
,cfg
.host
, &tm
);
3428 lgfp
= fopen(currlogfilename
, "r"); /* file already present? */
3432 i
= askappend(currlogfilename
);
3434 writemod
[0] = 'a'; /* set append mode */
3435 else if (i
== 0) { /* cancelled */
3437 cfg
.logtype
= 0; /* disable logging */
3442 lgfp
= fopen(currlogfilename
, writemod
);
3443 if (lgfp
) { /* enter into event log */
3444 sprintf(buf
, "%s session log (%s mode) to file : ",
3445 (writemod
[0] == 'a') ?
"Appending" : "Writing new",
3446 (cfg
.logtype
== LGTYP_ASCII ?
"ASCII" :
3447 cfg
.logtype
== LGTYP_DEBUG ?
"raw" : "<ukwn>"));
3448 /* Make sure we do not exceed the output buffer size */
3449 strncat(buf
, currlogfilename
, 128);
3450 buf
[strlen(buf
)] = '\0';
3453 /* --- write header line into log file */
3454 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp
);
3455 strftime(buf
, 24, "%Y.%m.%d %H:%M:%S", &tm
);
3457 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp
);
3461 void logfclose(void)
3470 * translate format codes into time/date strings
3471 * and insert them into log file name
3473 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3475 static void xlatlognam(char *d
, char *s
, char *hostname
, struct tm
*tm
) {
3476 char buf
[10], *bufp
;
3478 char *ds
= d
; /* save start pos. */
3479 int len
= FILENAME_MAX
-1;
3482 /* Let (bufp, len) be the string to append. */
3483 bufp
= buf
; /* don't usually override this */
3487 if (*s
) switch (c
= *s
++, tolower(c
)) {
3489 size
= strftime(buf
, sizeof(buf
), "%Y", tm
);
3492 size
= strftime(buf
, sizeof(buf
), "%m", tm
);
3495 size
= strftime(buf
, sizeof(buf
), "%d", tm
);
3498 size
= strftime(buf
, sizeof(buf
), "%H%M%S", tm
);
3502 size
= strlen(bufp
);
3516 memcpy(d
, bufp
, size
);