7300e96b1fc9c96582b7368f78fb6e944802b055
14 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15 #define CL_VT100 0x0002 /* VT100 */
16 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17 #define CL_VT102 0x0008 /* VT102 */
18 #define CL_VT220 0x0010 /* VT220 */
19 #define CL_VT320 0x0020 /* VT320 */
20 #define CL_VT420 0x0040 /* VT420 */
21 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
27 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
28 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29 #define TM_VT102 (TM_VT100AVO|CL_VT102)
30 #define TM_VT220 (TM_VT102|CL_VT220)
31 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
34 #define TM_PUTTY (0xFFFF)
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
49 static int compatibility_level
= TM_PUTTY
;
51 static tree234
*scrollback
; /* lines scrolled off top of screen */
52 static tree234
*screen
; /* lines on primary screen */
53 static tree234
*alt_screen
; /* lines on alternate screen */
54 static int disptop
; /* distance scrolled back (0 or -ve) */
56 static unsigned long *cpos
; /* cursor position (convenience) */
58 static unsigned long *disptext
; /* buffer of text on real screen */
59 static unsigned long *dispcurs
; /* location of cursor on real screen */
60 static unsigned long curstype
; /* type of cursor on real screen */
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
65 struct beeptime
*next
;
68 static struct beeptime
*beephead
, *beeptail
;
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
76 static unsigned long curr_attr
, save_attr
;
77 static unsigned long erase_char
= ERASE_CHAR
;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
89 static pos curs
; /* cursor */
90 static pos savecurs
; /* saved cursor position */
91 static int marg_t
, marg_b
; /* scroll margins */
92 static int dec_om
; /* DEC origin mode flag */
93 static int wrap
, wrapnext
; /* wrap flags */
94 static int insert
; /* insert-mode flag */
95 static int cset
; /* 0 or 1: which char set */
96 static int save_cset
, save_csattr
; /* saved with cursor position */
97 static int save_utf
; /* saved with cursor position */
98 static int rvideo
; /* global reverse video flag */
99 static int rvbell_timeout
; /* for ESC[?5hESC[?5l vbell */
100 static int cursor_on
; /* cursor enabled flag */
101 static int reset_132
; /* Flag ESC c resets to 80 cols */
102 static int use_bce
; /* Use Background coloured erase */
103 static int blinker
; /* When blinking is the cursor on ? */
104 static int tblinker
; /* When the blinking text is on */
105 static int blink_is_real
; /* Actually blink blinking text */
106 static int term_echoing
; /* Does terminal want local echo? */
107 static int term_editing
; /* Does terminal want local edit? */
108 static int sco_acs
, save_sco_acs
; /* CSI 10,11,12m -> OEM charset */
109 static int vt52_bold
; /* Force bold on non-bold colours */
110 static int utf_state
; /* Is there a pending UTF-8 character */
111 static int utf_char
; /* and what is it so far. */
112 static int utf_size
; /* The size of the UTF character. */
114 static int xterm_mouse
; /* send mouse messages to app */
116 static unsigned long cset_attr
[2];
119 * Saved settings on the alternate screen.
121 static int alt_x
, alt_y
, alt_om
, alt_wrap
, alt_wnext
, alt_ins
, alt_cset
, alt_sco_acs
, alt_utf
;
122 static int alt_t
, alt_b
;
123 static int alt_which
;
125 #define ARGS_MAX 32 /* max # of esc sequence arguments */
126 #define ARG_DEFAULT 0 /* if an arg isn't specified */
127 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
128 static int esc_args
[ARGS_MAX
];
129 static int esc_nargs
;
130 static int esc_query
;
131 #define ANSI(x,y) ((x)+((y)<<8))
132 #define ANSI_QUE(x) ANSI(x,TRUE)
134 #define OSC_STR_MAX 2048
135 static int osc_strlen
;
136 static char osc_string
[OSC_STR_MAX
+ 1];
139 static char id_string
[1024] = "\033[?6c";
141 static unsigned char *tabs
;
153 OSC_STRING
, OSC_MAYBE_ST
,
162 NO_SELECTION
, ABOUT_TO
, DRAGGING
, SELECTED
165 SM_CHAR
, SM_WORD
, SM_LINE
167 static pos selstart
, selend
, selanchor
;
169 static short wordness
[256] = {
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
172 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
174 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
176 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
177 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
182 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
184 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
185 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
188 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
189 static wchar_t sel_nl
[] = SEL_NL
;
190 static wchar_t *paste_buffer
= 0;
191 static int paste_len
, paste_pos
, paste_hold
;
194 * Internal prototypes.
196 static void do_paint(Context
, int);
197 static void erase_lots(int, int, int);
198 static void swap_screen(int);
199 static void update_sbar(void);
200 static void deselect(void);
201 /* log session to file stuff ... */
202 static FILE *lgfp
= NULL
;
203 static void logtraffic(unsigned char c
, int logmode
);
204 static void xlatlognam(char *d
, char *s
, char *hostname
, struct tm
*tm
);
207 * Resize a line to make it `cols' columns wide.
209 unsigned long *resizeline(unsigned long *line
, int cols
)
212 unsigned long lineattrs
;
214 if (line
[0] != (unsigned long)cols
) {
216 * This line is the wrong length, which probably means it
217 * hasn't been accessed since a resize. Resize it now.
220 lineattrs
= line
[oldlen
+ 1];
221 line
= srealloc(line
, TSIZE
* (2 + cols
));
223 for (i
= oldlen
; i
< cols
; i
++)
224 line
[i
+ 1] = ERASE_CHAR
;
225 line
[cols
+ 1] = lineattrs
& LATTR_MODE
;
232 * Retrieve a line of the screen or of the scrollback, according to
233 * whether the y coordinate is non-negative or negative
236 unsigned long *lineptr(int y
, int lineno
)
238 unsigned long *line
, *newline
;
246 whichtree
= scrollback
;
247 treeindex
= y
+ count234(scrollback
);
249 line
= index234(whichtree
, treeindex
);
251 /* We assume that we don't screw up and retrieve something out of range. */
252 assert(line
!= NULL
);
254 newline
= resizeline(line
, cols
);
255 if (newline
!= line
) {
256 delpos234(whichtree
, treeindex
);
257 addpos234(whichtree
, newline
, treeindex
);
264 #define lineptr(x) lineptr(x,__LINE__)
266 * Set up power-on settings for the terminal.
268 static void power_on(void)
270 curs
.x
= curs
.y
= alt_x
= alt_y
= savecurs
.x
= savecurs
.y
= 0;
273 alt_b
= marg_b
= rows
- 1;
278 for (i
= 0; i
< cols
; i
++)
279 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
281 alt_om
= dec_om
= cfg
.dec_om
;
282 alt_wnext
= wrapnext
= alt_ins
= insert
= FALSE
;
283 alt_wrap
= wrap
= cfg
.wrap_mode
;
286 alt_sco_acs
= sco_acs
= 0;
287 cset_attr
[0] = cset_attr
[1] = ATTR_ASCII
;
292 save_attr
= curr_attr
= ATTR_DEFAULT
;
293 term_editing
= term_echoing
= FALSE
;
294 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
295 app_cursor_keys
= cfg
.app_cursor
;
296 app_keypad_keys
= cfg
.app_keypad
;
298 blink_is_real
= cfg
.blinktext
;
299 erase_char
= ERASE_CHAR
;
303 for (i
= 0; i
< 256; i
++)
304 wordness
[i
] = cfg
.wordness
[i
];
308 erase_lots(FALSE
, TRUE
, TRUE
);
310 erase_lots(FALSE
, TRUE
, TRUE
);
315 * Force a screen update.
317 void term_update(void)
324 if ((seen_key_event
&& (cfg
.scroll_on_key
)) ||
325 (seen_disp_event
&& (cfg
.scroll_on_disp
))) {
326 disptop
= 0; /* return to main screen */
327 seen_disp_event
= seen_key_event
= 0;
330 sys_cursor(curs
.x
, curs
.y
- disptop
);
336 * Same as power_on(), but an external function.
338 void term_pwron(void)
348 * Clear the scrollback.
350 void term_clrsb(void)
354 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
361 * Initialise the terminal.
365 screen
= alt_screen
= scrollback
= NULL
;
367 disptext
= dispcurs
= NULL
;
372 beephead
= beeptail
= NULL
;
375 beep_overloaded
= FALSE
;
379 * Set up the terminal for a given size.
381 void term_size(int newrows
, int newcols
, int newsavelines
)
384 unsigned long *newdisp
, *line
;
387 int save_alt_which
= alt_which
;
389 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
390 return; /* nothing to do */
396 alt_b
= marg_b
= newrows
- 1;
399 scrollback
= newtree234(NULL
);
400 screen
= newtree234(NULL
);
405 * Resize the screen and scrollback. We only need to shift
406 * lines around within our data structures, because lineptr()
407 * will take care of resizing each individual line if
410 * - If the new screen and the old screen differ in length, we
411 * must shunt some lines in from the scrollback or out to
414 * - If doing that fails to provide us with enough material to
415 * fill the new screen (i.e. the number of rows needed in
416 * the new screen exceeds the total number in the previous
417 * screen+scrollback), we must invent some blank lines to
420 * - Then, if the new scrollback length is less than the
421 * amount of scrollback we actually have, we must throw some
424 sblen
= count234(scrollback
);
425 /* Do this loop to expand the screen if newrows > rows */
426 for (i
= rows
; i
< newrows
; i
++) {
428 line
= delpos234(scrollback
, --sblen
);
430 line
= smalloc(TSIZE
* (newcols
+ 2));
432 for (j
= 0; j
<= newcols
; j
++)
433 line
[j
+ 1] = ERASE_CHAR
;
435 addpos234(screen
, line
, 0);
437 /* Do this loop to shrink the screen if newrows < rows */
438 for (i
= newrows
; i
< rows
; i
++) {
439 line
= delpos234(screen
, 0);
440 addpos234(scrollback
, line
, sblen
++);
442 assert(count234(screen
) == newrows
);
443 while (sblen
> newsavelines
) {
444 line
= delpos234(scrollback
, 0);
448 assert(count234(scrollback
) <= newsavelines
);
451 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
452 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
453 newdisp
[i
] = ATTR_INVALID
;
458 newalt
= newtree234(NULL
);
459 for (i
= 0; i
< newrows
; i
++) {
460 line
= smalloc(TSIZE
* (newcols
+ 2));
462 for (j
= 0; j
<= newcols
; j
++)
463 line
[j
+ 1] = erase_char
;
464 addpos234(newalt
, line
, i
);
467 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
469 freetree234(alt_screen
);
473 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
476 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
477 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
481 curs
.y
+= newrows
- rows
;
484 if (curs
.y
>= newrows
)
485 curs
.y
= newrows
- 1;
486 if (curs
.x
>= newcols
)
487 curs
.x
= newcols
- 1;
489 wrapnext
= alt_wnext
= FALSE
;
493 savelines
= newsavelines
;
496 swap_screen(save_alt_which
);
505 static void swap_screen(int which
)
510 if (which
== alt_which
)
537 wrapnext
= alt_wnext
;
549 sco_acs
= alt_sco_acs
;
556 * Update the scroll bar.
558 static void update_sbar(void)
562 nscroll
= count234(scrollback
);
564 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
568 * Check whether the region bounded by the two pointers intersects
569 * the scroll region, and de-select the on-screen selection if so.
571 static void check_selection(pos from
, pos to
)
573 if (poslt(from
, selend
) && poslt(selstart
, to
))
578 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
579 * for backward.) `sb' is TRUE if the scrolling is permitted to
580 * affect the scrollback buffer.
582 * NB this function invalidates all pointers into lines of the
583 * screen data structures. In particular, you MUST call fix_cpos
584 * after calling scroll() and before doing anything else that
585 * uses the cpos shortcut pointer.
587 static void scroll(int topline
, int botline
, int lines
, int sb
)
589 unsigned long *line
, *line2
;
592 if (topline
!= 0 || alt_which
!= 0)
597 line
= delpos234(screen
, botline
);
598 line
= resizeline(line
, cols
);
599 for (i
= 0; i
< cols
; i
++)
600 line
[i
+ 1] = erase_char
;
602 addpos234(screen
, line
, topline
);
604 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
606 if (selstart
.y
> botline
) {
607 selstart
.y
= botline
;
611 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
613 if (selend
.y
> botline
) {
623 line
= delpos234(screen
, topline
);
624 if (sb
&& savelines
> 0) {
625 int sblen
= count234(scrollback
);
627 * We must add this line to the scrollback. We'll
628 * remove a line from the top of the scrollback to
629 * replace it, or allocate a new one if the
630 * scrollback isn't full.
632 if (sblen
== savelines
) {
633 sblen
--, line2
= delpos234(scrollback
, 0);
635 line2
= smalloc(TSIZE
* (cols
+ 2));
638 addpos234(scrollback
, line
, sblen
);
642 * If the user is currently looking at part of the
643 * scrollback, and they haven't enabled any options
644 * that are going to reset the scrollback as a
645 * result of this movement, then the chances are
646 * they'd like to keep looking at the same line. So
647 * we move their viewpoint at the same rate as the
648 * scroll, at least until their viewpoint hits the
649 * top end of the scrollback buffer, at which point
650 * we don't have the choice any more.
652 * Thanks to Jan Holmen Holsten for the idea and
653 * initial implementation.
655 if (disptop
> -savelines
&& disptop
< 0)
658 line
= resizeline(line
, cols
);
659 for (i
= 0; i
< cols
; i
++)
660 line
[i
+ 1] = erase_char
;
662 addpos234(screen
, line
, botline
);
665 * If the selection endpoints move into the scrollback,
666 * we keep them moving until they hit the top. However,
667 * of course, if the line _hasn't_ moved into the
668 * scrollback then we don't do this, and cut them off
669 * at the top of the scroll region.
671 seltop
= sb ?
-savelines
: 0;
673 if (selstart
.y
>= seltop
&& selstart
.y
<= botline
) {
675 if (selstart
.y
< seltop
) {
680 if (selend
.y
>= seltop
&& selend
.y
<= botline
) {
682 if (selend
.y
< seltop
) {
694 * Move the cursor to a given position, clipping at boundaries. We
695 * may or may not want to clip at the scroll margin: marg_clip is 0
696 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
697 * even _being_ outside the margins.
699 static void move(int x
, int y
, int marg_clip
)
706 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
708 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
722 * Save or restore the cursor and SGR mode.
724 static void save_cursor(int save
)
728 save_attr
= curr_attr
;
731 save_csattr
= cset_attr
[cset
];
732 save_sco_acs
= sco_acs
;
735 /* Make sure the window hasn't shrunk since the save */
741 curr_attr
= save_attr
;
744 cset_attr
[cset
] = save_csattr
;
745 sco_acs
= save_sco_acs
;
748 erase_char
= (' ' | ATTR_ASCII
|
749 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
754 * Erase a large portion of the screen: the whole screen, or the
755 * whole line, or parts thereof.
757 static void erase_lots(int line_only
, int from_begin
, int to_end
)
761 unsigned long *ldata
;
783 check_selection(start
, end
);
785 /* Clear screen also forces a full window redraw, just in case. */
786 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
789 ldata
= lineptr(start
.y
);
790 while (poslt(start
, end
)) {
791 if (start
.x
== cols
&& !erase_lattr
)
792 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
794 ldata
[start
.x
] = erase_char
;
795 if (incpos(start
) && start
.y
< rows
)
796 ldata
= lineptr(start
.y
);
801 * Insert or delete characters within the current line. n is +ve if
802 * insertion is desired, and -ve for deletion.
804 static void insch(int n
)
806 int dir
= (n
< 0 ?
-1 : +1);
809 unsigned long *ldata
;
811 n
= (n
< 0 ?
-n
: n
);
812 if (n
> cols
- curs
.x
)
814 m
= cols
- curs
.x
- n
;
816 cursplus
.x
= curs
.x
+ n
;
817 check_selection(curs
, cursplus
);
818 ldata
= lineptr(curs
.y
);
820 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
822 ldata
[curs
.x
+ m
++] = erase_char
;
824 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
826 ldata
[curs
.x
+ n
] = erase_char
;
831 * Toggle terminal mode `mode' to state `state'. (`query' indicates
832 * whether the mode is a DEC private one or a normal one.)
834 static void toggle_mode(int mode
, int query
, int state
)
840 case 1: /* application cursor keys */
841 app_cursor_keys
= state
;
843 case 2: /* VT52 mode */
846 blink_is_real
= FALSE
;
849 blink_is_real
= cfg
.blinktext
;
852 case 3: /* 80/132 columns */
854 request_resize(state ?
132 : 80, rows
, 1);
857 case 5: /* reverse video */
859 * Toggle reverse video. If we receive an OFF within the
860 * visual bell timeout period after an ON, we trigger an
861 * effective visual bell, so that ESC[?5hESC[?5l will
862 * always be an actually _visible_ visual bell.
864 ticks
= GetTickCount();
865 if (rvideo
&& !state
&& /* we're turning it off */
866 ticks
< rvbell_timeout
) { /* and it's not long since it was turned on */
867 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
868 if (vbell_timeout
< rvbell_timeout
) /* don't move vbell end forward */
869 vbell_timeout
= rvbell_timeout
; /* vbell end is at least then */
870 } else if (!rvideo
&& state
) {
871 /* This is an ON, so we notice the time and save it. */
872 rvbell_timeout
= ticks
+ VBELL_TIMEOUT
;
875 seen_disp_event
= TRUE
;
879 case 6: /* DEC origin mode */
882 case 7: /* auto wrap */
885 case 8: /* auto key repeat */
888 case 10: /* set local edit mode */
889 term_editing
= state
;
890 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
892 case 25: /* enable/disable cursor */
893 compatibility2(OTHER
, VT220
);
895 seen_disp_event
= TRUE
;
897 case 47: /* alternate screen */
898 compatibility(OTHER
);
903 case 1000: /* xterm mouse 1 */
904 xterm_mouse
= state ?
1 : 0;
905 set_raw_mouse_mode(state
);
907 case 1002: /* xterm mouse 2 */
908 xterm_mouse
= state ?
2 : 0;
909 set_raw_mouse_mode(state
);
913 case 4: /* set insert mode */
914 compatibility(VT102
);
917 case 12: /* set echo mode */
918 term_echoing
= !state
;
919 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
921 case 20: /* Return sends ... */
922 cr_lf_return
= state
;
924 case 34: /* Make cursor BIG */
925 compatibility2(OTHER
, VT220
);
931 * Process an OSC sequence: set window title or icon name.
933 static void do_osc(void)
937 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
939 osc_string
[osc_strlen
] = '\0';
940 switch (esc_args
[0]) {
943 set_icon(osc_string
);
944 if (esc_args
[0] == 1)
946 /* fall through: parameter 0 means set both */
949 set_title(osc_string
);
956 * Remove everything currently in `inbuf' and stick it up on the
957 * in-memory display. There's a big state machine in here to
958 * process escape sequences...
965 * Optionally log the session traffic to a file. Useful for
966 * debugging and possibly also useful for actual logging.
968 if (cfg
.logtype
== LGTYP_DEBUG
)
969 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
970 logtraffic((unsigned char) inbuf
[inbuf_reap
], LGTYP_DEBUG
);
973 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
974 c
= inbuf
[inbuf_reap
];
976 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
977 * be able to display 8-bit characters, but I'll let that go 'cause
981 /* First see about all those translations. */
982 if (termstate
== TOPLEVEL
) {
987 /* UTF-8 must be stateless so we ignore iso2022. */
988 if (unitab_ctrl
[c
] != 0xFF)
990 else c
= ((unsigned char)c
) | ATTR_ASCII
;
992 } else if ((c
& 0xe0) == 0xc0) {
993 utf_size
= utf_state
= 1;
994 utf_char
= (c
& 0x1f);
995 } else if ((c
& 0xf0) == 0xe0) {
996 utf_size
= utf_state
= 2;
997 utf_char
= (c
& 0x0f);
998 } else if ((c
& 0xf8) == 0xf0) {
999 utf_size
= utf_state
= 3;
1000 utf_char
= (c
& 0x07);
1001 } else if ((c
& 0xfc) == 0xf8) {
1002 utf_size
= utf_state
= 4;
1003 utf_char
= (c
& 0x03);
1004 } else if ((c
& 0xfe) == 0xfc) {
1005 utf_size
= utf_state
= 5;
1006 utf_char
= (c
& 0x01);
1017 if ((c
& 0xC0) != 0x80) {
1023 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
1029 /* Is somebody trying to be evil! */
1031 (c
< 0x800 && utf_size
>= 2) ||
1032 (c
< 0x10000 && utf_size
>= 3) ||
1033 (c
< 0x200000 && utf_size
>= 4) ||
1034 (c
< 0x4000000 && utf_size
>= 5))
1037 /* Unicode line separator and paragraph separator are CR-LF */
1038 if (c
== 0x2028 || c
== 0x2029)
1041 /* High controls are probably a Baaad idea too. */
1045 /* The UTF-16 surrogates are not nice either. */
1046 /* The standard give the option of decoding these:
1047 * I don't want to! */
1048 if (c
>= 0xD800 && c
< 0xE000)
1051 /* ISO 10646 characters now limited to UTF-16 range. */
1055 /* This is currently a TagPhobic application.. */
1056 if (c
>= 0xE0000 && c
<= 0xE007F)
1059 /* U+FEFF is best seen as a null. */
1062 /* But U+FFFE is an error. */
1063 if (c
== 0xFFFE || c
== 0xFFFF)
1066 /* Oops this is a 16bit implementation */
1071 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1073 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1075 if (sco_acs
== 2) c
^= 0x80;
1078 switch (cset_attr
[cset
]) {
1080 * Linedraw characters are different from 'ESC ( B'
1081 * only for a small range. For ones outside that
1082 * range, make sure we use the same font as well as
1083 * the same encoding.
1086 if (unitab_ctrl
[c
] != 0xFF)
1089 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1093 /* If UK-ASCII, make the '#' a LineDraw Pound */
1095 c
= '}' | ATTR_LINEDRW
;
1098 /*FALLTHROUGH*/ case ATTR_ASCII
:
1099 if (unitab_ctrl
[c
] != 0xFF)
1102 c
= ((unsigned char) c
) | ATTR_ASCII
;
1105 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1111 /* How about C1 controls ? */
1112 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1113 has_compat(VT220
)) {
1114 termstate
= SEEN_ESC
;
1116 c
= '@' + (c
& 0x1F);
1119 /* Or the GL control. */
1120 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1121 if (curs
.x
&& !wrapnext
)
1125 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1127 /* Or normal C0 controls. */
1128 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1130 case '\005': /* terminal type query */
1131 /* Strictly speaking this is VT100 but a VT100 defaults to
1132 * no response. Other terminals respond at their option.
1134 * Don't put a CR in the default string as this tends to
1135 * upset some weird software.
1137 * An xterm returns "xterm" (5 characters)
1139 compatibility(ANSIMIN
);
1141 char abuf
[256], *s
, *d
;
1143 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1145 if (*s
>= 'a' && *s
<= 'z')
1146 *d
++ = (*s
- ('a' - 1));
1147 else if ((*s
>= '@' && *s
<= '_') ||
1148 *s
== '?' || (*s
& 0x80))
1153 } else if (*s
== '^') {
1158 lpage_send(CP_ACP
, abuf
, d
- abuf
);
1163 struct beeptime
*newbeep
;
1166 ticks
= GetTickCount();
1168 if (!beep_overloaded
) {
1169 newbeep
= smalloc(sizeof(struct beeptime
));
1170 newbeep
->ticks
= ticks
;
1171 newbeep
->next
= NULL
;
1175 beeptail
->next
= newbeep
;
1181 * Throw out any beeps that happened more than
1185 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1186 struct beeptime
*tmp
= beephead
;
1187 beephead
= tmp
->next
;
1194 if (cfg
.bellovl
&& beep_overloaded
&&
1195 ticks
- lastbeep
>= cfg
.bellovl_s
) {
1197 * If we're currently overloaded and the
1198 * last beep was more than s seconds ago,
1199 * leave overload mode.
1201 beep_overloaded
= FALSE
;
1202 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1203 nbeeps
>= cfg
.bellovl_n
) {
1205 * Now, if we have n or more beeps
1206 * remaining in the queue, go into overload
1209 beep_overloaded
= TRUE
;
1214 * Perform an actual beep if we're not overloaded.
1216 if (!cfg
.bellovl
|| !beep_overloaded
) {
1218 if (cfg
.beep
== BELL_VISUAL
) {
1220 vbell_timeout
= ticks
+ VBELL_TIMEOUT
;
1228 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1229 else if (curs
.x
== 0 && curs
.y
> 0)
1230 curs
.x
= cols
- 1, curs
.y
--;
1236 seen_disp_event
= TRUE
;
1239 compatibility(VT100
);
1243 compatibility(VT100
);
1248 termstate
= VT52_ESC
;
1250 compatibility(ANSIMIN
);
1251 termstate
= SEEN_ESC
;
1259 seen_disp_event
= TRUE
;
1261 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1264 if (has_compat(SCOANSI
)) {
1266 erase_lots(FALSE
, FALSE
, TRUE
);
1269 seen_disp_event
= 1;
1273 compatibility(VT100
);
1275 if (curs
.y
== marg_b
)
1276 scroll(marg_t
, marg_b
, 1, TRUE
);
1277 else if (curs
.y
< rows
- 1)
1283 seen_disp_event
= 1;
1285 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1289 pos old_curs
= curs
;
1290 unsigned long *ldata
= lineptr(curs
.y
);
1294 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1296 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1297 if (curs
.x
>= cols
/ 2)
1298 curs
.x
= cols
/ 2 - 1;
1305 check_selection(old_curs
, curs
);
1307 seen_disp_event
= TRUE
;
1311 switch (termstate
) {
1313 /* Only graphic characters get this far, ctrls are stripped above */
1314 if (wrapnext
&& wrap
) {
1315 cpos
[1] |= LATTR_WRAPPED
;
1316 if (curs
.y
== marg_b
)
1317 scroll(marg_t
, marg_b
, 1, TRUE
);
1318 else if (curs
.y
< rows
- 1)
1326 if (selstate
!= NO_SELECTION
) {
1327 pos cursplus
= curs
;
1329 check_selection(curs
, cursplus
);
1331 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1332 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1334 extern int wcwidth(wchar_t ucs
);
1339 width
= wcwidth((wchar_t) c
);
1342 if (curs
.x
+ 1 != cols
) {
1343 *cpos
++ = c
| ATTR_WIDE
| curr_attr
;
1344 *cpos
++ = UCSWIDE
| curr_attr
;
1349 *cpos
++ = c
| curr_attr
;
1356 if (curs
.x
== cols
) {
1360 if (wrap
&& vt52_mode
) {
1361 cpos
[1] |= LATTR_WRAPPED
;
1362 if (curs
.y
== marg_b
)
1363 scroll(marg_t
, marg_b
, 1, TRUE
);
1364 else if (curs
.y
< rows
- 1)
1371 seen_disp_event
= 1;
1376 * This state is virtually identical to SEEN_ESC, with the
1377 * exception that we have an OSC sequence in the pipeline,
1378 * and _if_ we see a backslash, we process it.
1382 termstate
= TOPLEVEL
;
1385 /* else fall through */
1387 if (c
>= ' ' && c
<= '/') {
1394 termstate
= TOPLEVEL
;
1395 switch (ANSI(c
, esc_query
)) {
1396 case '[': /* enter CSI mode */
1397 termstate
= SEEN_CSI
;
1399 esc_args
[0] = ARG_DEFAULT
;
1402 case ']': /* xterm escape sequences */
1403 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1404 compatibility(OTHER
);
1405 termstate
= SEEN_OSC
;
1408 case '7': /* save cursor */
1409 compatibility(VT100
);
1412 case '8': /* restore cursor */
1413 compatibility(VT100
);
1415 seen_disp_event
= TRUE
;
1418 compatibility(VT100
);
1419 app_keypad_keys
= TRUE
;
1422 compatibility(VT100
);
1423 app_keypad_keys
= FALSE
;
1425 case 'D': /* exactly equivalent to LF */
1426 compatibility(VT100
);
1427 if (curs
.y
== marg_b
)
1428 scroll(marg_t
, marg_b
, 1, TRUE
);
1429 else if (curs
.y
< rows
- 1)
1433 seen_disp_event
= TRUE
;
1435 case 'E': /* exactly equivalent to CR-LF */
1436 compatibility(VT100
);
1438 if (curs
.y
== marg_b
)
1439 scroll(marg_t
, marg_b
, 1, TRUE
);
1440 else if (curs
.y
< rows
- 1)
1444 seen_disp_event
= TRUE
;
1446 case 'M': /* reverse index - backwards LF */
1447 compatibility(VT100
);
1448 if (curs
.y
== marg_t
)
1449 scroll(marg_t
, marg_b
, -1, TRUE
);
1450 else if (curs
.y
> 0)
1454 seen_disp_event
= TRUE
;
1456 case 'Z': /* terminal type query */
1457 compatibility(VT100
);
1458 ldisc_send(id_string
, strlen(id_string
));
1460 case 'c': /* restore power-on settings */
1461 compatibility(VT100
);
1464 request_resize(80, rows
, 1);
1469 seen_disp_event
= TRUE
;
1471 case 'H': /* set a tab */
1472 compatibility(VT100
);
1473 tabs
[curs
.x
] = TRUE
;
1476 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1477 compatibility(VT100
);
1479 unsigned long *ldata
;
1483 for (i
= 0; i
< rows
; i
++) {
1485 for (j
= 0; j
< cols
; j
++)
1486 ldata
[j
] = ATTR_DEFAULT
| 'E';
1490 seen_disp_event
= TRUE
;
1491 scrtop
.x
= scrtop
.y
= 0;
1494 check_selection(scrtop
, scrbot
);
1498 case ANSI('3', '#'):
1499 case ANSI('4', '#'):
1500 case ANSI('5', '#'):
1501 case ANSI('6', '#'):
1502 compatibility(VT100
);
1504 unsigned long nlattr
;
1505 unsigned long *ldata
;
1506 switch (ANSI(c
, esc_query
)) {
1507 case ANSI('3', '#'):
1510 case ANSI('4', '#'):
1513 case ANSI('5', '#'):
1514 nlattr
= LATTR_NORM
;
1516 default: /* spiritually case ANSI('6', '#'): */
1517 nlattr
= LATTR_WIDE
;
1520 ldata
= lineptr(curs
.y
);
1521 ldata
[cols
] &= ~LATTR_MODE
;
1522 ldata
[cols
] |= nlattr
;
1526 case ANSI('A', '('):
1527 compatibility(VT100
);
1528 cset_attr
[0] = ATTR_GBCHR
;
1530 case ANSI('B', '('):
1531 compatibility(VT100
);
1532 cset_attr
[0] = ATTR_ASCII
;
1534 case ANSI('0', '('):
1535 compatibility(VT100
);
1536 cset_attr
[0] = ATTR_LINEDRW
;
1538 case ANSI('U', '('):
1539 compatibility(OTHER
);
1540 cset_attr
[0] = ATTR_SCOACS
;
1543 case ANSI('A', ')'):
1544 compatibility(VT100
);
1545 cset_attr
[1] = ATTR_GBCHR
;
1547 case ANSI('B', ')'):
1548 compatibility(VT100
);
1549 cset_attr
[1] = ATTR_ASCII
;
1551 case ANSI('0', ')'):
1552 compatibility(VT100
);
1553 cset_attr
[1] = ATTR_LINEDRW
;
1555 case ANSI('U', ')'):
1556 compatibility(OTHER
);
1557 cset_attr
[1] = ATTR_SCOACS
;
1560 case ANSI('8', '%'): /* Old Linux code */
1561 case ANSI('G', '%'):
1562 compatibility(OTHER
);
1565 case ANSI('@', '%'):
1566 compatibility(OTHER
);
1572 termstate
= TOPLEVEL
; /* default */
1574 if (esc_nargs
<= ARGS_MAX
) {
1575 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1576 esc_args
[esc_nargs
- 1] = 0;
1577 esc_args
[esc_nargs
- 1] =
1578 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1580 termstate
= SEEN_CSI
;
1581 } else if (c
== ';') {
1582 if (++esc_nargs
<= ARGS_MAX
)
1583 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1584 termstate
= SEEN_CSI
;
1585 } else if (c
< '@') {
1592 termstate
= SEEN_CSI
;
1594 switch (ANSI(c
, esc_query
)) {
1595 case 'A': /* move up N lines */
1596 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1597 seen_disp_event
= TRUE
;
1599 case 'e': /* move down N lines */
1600 compatibility(ANSI
);
1603 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1604 seen_disp_event
= TRUE
;
1606 case ANSI('c', '>'): /* report xterm version */
1607 compatibility(OTHER
);
1608 /* this reports xterm version 136 so that VIM can
1609 use the drag messages from the mouse reporting */
1610 ldisc_send("\033[>0;136;0c", 11);
1612 case 'a': /* move right N cols */
1613 compatibility(ANSI
);
1616 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1617 seen_disp_event
= TRUE
;
1619 case 'D': /* move left N cols */
1620 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1621 seen_disp_event
= TRUE
;
1623 case 'E': /* move down N lines and CR */
1624 compatibility(ANSI
);
1625 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1626 seen_disp_event
= TRUE
;
1628 case 'F': /* move up N lines and CR */
1629 compatibility(ANSI
);
1630 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1631 seen_disp_event
= TRUE
;
1634 case '`': /* set horizontal posn */
1635 compatibility(ANSI
);
1636 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1637 seen_disp_event
= TRUE
;
1639 case 'd': /* set vertical posn */
1640 compatibility(ANSI
);
1642 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1645 seen_disp_event
= TRUE
;
1648 case 'f': /* set horz and vert posns at once */
1650 esc_args
[1] = ARG_DEFAULT
;
1651 move(def(esc_args
[1], 1) - 1,
1652 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1655 seen_disp_event
= TRUE
;
1657 case 'J': /* erase screen or parts of it */
1659 unsigned int i
= def(esc_args
[0], 0) + 1;
1662 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1665 seen_disp_event
= TRUE
;
1667 case 'K': /* erase line or parts of it */
1669 unsigned int i
= def(esc_args
[0], 0) + 1;
1672 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1674 seen_disp_event
= TRUE
;
1676 case 'L': /* insert lines */
1677 compatibility(VT102
);
1678 if (curs
.y
<= marg_b
)
1679 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1682 seen_disp_event
= TRUE
;
1684 case 'M': /* delete lines */
1685 compatibility(VT102
);
1686 if (curs
.y
<= marg_b
)
1687 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1690 seen_disp_event
= TRUE
;
1692 case '@': /* insert chars */
1693 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1694 compatibility(VT102
);
1695 insch(def(esc_args
[0], 1));
1696 seen_disp_event
= TRUE
;
1698 case 'P': /* delete chars */
1699 compatibility(VT102
);
1700 insch(-def(esc_args
[0], 1));
1701 seen_disp_event
= TRUE
;
1703 case 'c': /* terminal type query */
1704 compatibility(VT100
);
1705 /* This is the response for a VT102 */
1706 ldisc_send(id_string
, strlen(id_string
));
1708 case 'n': /* cursor position query */
1709 if (esc_args
[0] == 6) {
1711 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1713 ldisc_send(buf
, strlen(buf
));
1714 } else if (esc_args
[0] == 5) {
1715 ldisc_send("\033[0n", 4);
1718 case 'h': /* toggle modes to high */
1720 compatibility(VT100
);
1723 for (i
= 0; i
< esc_nargs
; i
++)
1724 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1727 case 'l': /* toggle modes to low */
1729 compatibility(VT100
);
1732 for (i
= 0; i
< esc_nargs
; i
++)
1733 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1736 case 'g': /* clear tabs */
1737 compatibility(VT100
);
1738 if (esc_nargs
== 1) {
1739 if (esc_args
[0] == 0) {
1740 tabs
[curs
.x
] = FALSE
;
1741 } else if (esc_args
[0] == 3) {
1743 for (i
= 0; i
< cols
; i
++)
1748 case 'r': /* set scroll margins */
1749 compatibility(VT100
);
1750 if (esc_nargs
<= 2) {
1752 top
= def(esc_args
[0], 1) - 1;
1753 bot
= (esc_nargs
<= 1
1755 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1758 /* VTTEST Bug 9 - if region is less than 2 lines
1759 * don't change region.
1761 if (bot
- top
> 0) {
1766 * I used to think the cursor should be
1767 * placed at the top of the newly marginned
1768 * area. Apparently not: VMS TPU falls over
1771 * Well actually it should for Origin mode - RDB
1773 curs
.y
= (dec_om ? marg_t
: 0);
1775 seen_disp_event
= TRUE
;
1779 case 'm': /* set graphics rendition */
1782 * A VT100 without the AVO only had one attribute, either
1783 * underline or reverse video depending on the cursor type,
1784 * this was selected by CSI 7m.
1787 * This is sometimes DIM, eg on the GIGI and Linux
1789 * This is sometimes INVIS various ANSI.
1791 * This like 22 disables BOLD, DIM and INVIS
1793 * The ANSI colours appear on any terminal that has colour
1794 * (obviously) but the interaction between sgr0 and the
1795 * colours varies but is usually related to the background
1796 * colour erase item.
1797 * The interaction between colour attributes and the mono
1798 * ones is also very implementation dependent.
1800 * The 39 and 49 attributes are likely to be unimplemented.
1803 for (i
= 0; i
< esc_nargs
; i
++) {
1804 switch (def(esc_args
[i
], 0)) {
1805 case 0: /* restore defaults */
1806 curr_attr
= ATTR_DEFAULT
;
1808 case 1: /* enable bold */
1809 compatibility(VT100AVO
);
1810 curr_attr
|= ATTR_BOLD
;
1812 case 21: /* (enable double underline) */
1813 compatibility(OTHER
);
1814 case 4: /* enable underline */
1815 compatibility(VT100AVO
);
1816 curr_attr
|= ATTR_UNDER
;
1818 case 5: /* enable blink */
1819 compatibility(VT100AVO
);
1820 curr_attr
|= ATTR_BLINK
;
1822 case 7: /* enable reverse video */
1823 curr_attr
|= ATTR_REVERSE
;
1825 case 10: /* SCO acs off */
1826 compatibility(SCOANSI
);
1828 case 11: /* SCO acs on */
1829 compatibility(SCOANSI
);
1831 case 12: /* SCO acs on flipped */
1832 compatibility(SCOANSI
);
1834 case 22: /* disable bold */
1835 compatibility2(OTHER
, VT220
);
1836 curr_attr
&= ~ATTR_BOLD
;
1838 case 24: /* disable underline */
1839 compatibility2(OTHER
, VT220
);
1840 curr_attr
&= ~ATTR_UNDER
;
1842 case 25: /* disable blink */
1843 compatibility2(OTHER
, VT220
);
1844 curr_attr
&= ~ATTR_BLINK
;
1846 case 27: /* disable reverse video */
1847 compatibility2(OTHER
, VT220
);
1848 curr_attr
&= ~ATTR_REVERSE
;
1859 curr_attr
&= ~ATTR_FGMASK
;
1861 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1863 case 39: /* default-foreground */
1864 curr_attr
&= ~ATTR_FGMASK
;
1865 curr_attr
|= ATTR_DEFFG
;
1876 curr_attr
&= ~ATTR_BGMASK
;
1878 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1880 case 49: /* default-background */
1881 curr_attr
&= ~ATTR_BGMASK
;
1882 curr_attr
|= ATTR_DEFBG
;
1887 erase_char
= (' ' | ATTR_ASCII
|
1889 (ATTR_FGMASK
| ATTR_BGMASK
)));
1892 case 's': /* save cursor */
1895 case 'u': /* restore cursor */
1897 seen_disp_event
= TRUE
;
1899 case 't': /* set page size - ie window height */
1901 * VT340/VT420 sequence DECSLPP, DEC only allows values
1902 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1903 * illegal values (eg first arg 1..9) for window changing
1906 compatibility(VT340TEXT
);
1908 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1909 request_resize(cols
, def(esc_args
[0], 24), 0);
1914 compatibility(SCOANSI
);
1915 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
1918 seen_disp_event
= TRUE
;
1921 compatibility(SCOANSI
);
1922 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
1925 seen_disp_event
= TRUE
;
1927 case ANSI('|', '*'):
1928 /* VT420 sequence DECSNLS
1929 * Set number of lines on screen
1930 * VT420 uses VGA like hardware and can support any size in
1931 * reasonable range (24..49 AIUI) with no default specified.
1933 compatibility(VT420
);
1934 if (esc_nargs
== 1 && esc_args
[0] > 0) {
1935 request_resize(cols
,
1936 def(esc_args
[0], cfg
.height
),
1941 case ANSI('|', '$'):
1942 /* VT340/VT420 sequence DECSCPP
1943 * Set number of columns per page
1944 * Docs imply range is only 80 or 132, but I'll allow any.
1946 compatibility(VT340TEXT
);
1947 if (esc_nargs
<= 1) {
1948 request_resize(def(esc_args
[0], cfg
.width
),
1953 case 'X': /* write N spaces w/o moving cursor */
1954 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1955 compatibility(ANSIMIN
);
1957 int n
= def(esc_args
[0], 1);
1959 unsigned long *p
= cpos
;
1960 if (n
> cols
- curs
.x
)
1964 check_selection(curs
, cursplus
);
1967 seen_disp_event
= TRUE
;
1970 case 'x': /* report terminal characteristics */
1971 compatibility(VT100
);
1974 int i
= def(esc_args
[0], 0);
1975 if (i
== 0 || i
== 1) {
1976 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
1978 ldisc_send(buf
, 20);
1982 case 'Z': /* BackTab for xterm */
1983 compatibility(OTHER
);
1985 int i
= def(esc_args
[0], 1);
1986 pos old_curs
= curs
;
1988 for(;i
>0 && curs
.x
>0; i
--) {
1991 } while (curs
.x
>0 && !tabs
[curs
.x
]);
1994 check_selection(old_curs
, curs
);
1997 case ANSI('L', '='):
1998 compatibility(OTHER
);
1999 use_bce
= (esc_args
[0] <= 0);
2000 erase_char
= ERASE_CHAR
;
2002 erase_char
= (' ' | ATTR_ASCII
|
2004 (ATTR_FGMASK
| ATTR_BGMASK
)));
2006 case ANSI('E', '='):
2007 compatibility(OTHER
);
2008 blink_is_real
= (esc_args
[0] >= 1);
2010 case ANSI('p', '"'):
2011 /* Allow the host to make this emulator a 'perfect' VT102.
2012 * This first appeared in the VT220, but we do need to get
2013 * back to PuTTY mode so I won't check it.
2015 * The arg in 40..42,50 are a PuTTY extension.
2016 * The 2nd arg, 8bit vs 7bit is not checked.
2018 * Setting VT102 mode should also change the Fkeys to
2019 * generate PF* codes as a real VT102 has no Fkeys.
2020 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2023 * Note ESC c will NOT change this!
2026 switch (esc_args
[0]) {
2028 compatibility_level
&= ~TM_VTXXX
;
2029 compatibility_level
|= TM_VT102
;
2032 compatibility_level
&= ~TM_VTXXX
;
2033 compatibility_level
|= TM_VT220
;
2037 if (esc_args
[0] > 60 && esc_args
[0] < 70)
2038 compatibility_level
|= TM_VTXXX
;
2042 compatibility_level
&= TM_VTXXX
;
2045 compatibility_level
= TM_PUTTY
;
2048 compatibility_level
= TM_SCOANSI
;
2052 compatibility_level
= TM_PUTTY
;
2058 /* Change the response to CSI c */
2059 if (esc_args
[0] == 50) {
2062 strcpy(id_string
, "\033[?");
2063 for (i
= 1; i
< esc_nargs
; i
++) {
2065 strcat(id_string
, ";");
2066 sprintf(lbuf
, "%d", esc_args
[i
]);
2067 strcat(id_string
, lbuf
);
2069 strcat(id_string
, "c");
2072 /* Is this a good idea ?
2073 * Well we should do a soft reset at this point ...
2075 if (!has_compat(VT420
) && has_compat(VT100
)) {
2077 request_resize(132, 24, 1);
2079 request_resize(80, 24, 1);
2088 case 'P': /* Linux palette sequence */
2089 termstate
= SEEN_OSC_P
;
2092 case 'R': /* Linux palette reset */
2095 termstate
= TOPLEVEL
;
2097 case 'W': /* word-set */
2098 termstate
= SEEN_OSC_W
;
2111 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2115 * Grotty hack to support xterm and DECterm title
2116 * sequences concurrently.
2118 if (esc_args
[0] == 2) {
2122 /* else fall through */
2124 termstate
= OSC_STRING
;
2130 * This OSC stuff is EVIL. It takes just one character to get into
2131 * sysline mode and it's not initially obvious how to get out.
2132 * So I've added CR and LF as string aborts.
2133 * This shouldn't effect compatibility as I believe embedded
2134 * control characters are supposed to be interpreted (maybe?)
2135 * and they don't display anything useful anyway.
2139 if (c
== '\n' || c
== '\r') {
2140 termstate
= TOPLEVEL
;
2141 } else if (c
== 0234 || c
== '\007') {
2143 * These characters terminate the string; ST and BEL
2144 * terminate the sequence and trigger instant
2145 * processing of it, whereas ESC goes back to SEEN_ESC
2146 * mode unless it is followed by \, in which case it is
2147 * synonymous with ST in the first place.
2150 termstate
= TOPLEVEL
;
2151 } else if (c
== '\033')
2152 termstate
= OSC_MAYBE_ST
;
2153 else if (osc_strlen
< OSC_STR_MAX
)
2154 osc_string
[osc_strlen
++] = c
;
2158 int max
= (osc_strlen
== 0 ?
21 : 16);
2160 if (c
>= '0' && c
<= '9')
2162 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2164 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2167 termstate
= TOPLEVEL
;
2170 osc_string
[osc_strlen
++] = val
;
2171 if (osc_strlen
>= 7) {
2172 palette_set(osc_string
[0],
2173 osc_string
[1] * 16 + osc_string
[2],
2174 osc_string
[3] * 16 + osc_string
[4],
2175 osc_string
[5] * 16 + osc_string
[6]);
2177 termstate
= TOPLEVEL
;
2193 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2196 termstate
= OSC_STRING
;
2201 termstate
= TOPLEVEL
;
2202 seen_disp_event
= TRUE
;
2205 move(curs
.x
, curs
.y
- 1, 1);
2208 move(curs
.x
, curs
.y
+ 1, 1);
2211 move(curs
.x
+ 1, curs
.y
, 1);
2214 move(curs
.x
- 1, curs
.y
, 1);
2217 * From the VT100 Manual
2218 * NOTE: The special graphics characters in the VT100
2219 * are different from those in the VT52
2221 * From VT102 manual:
2222 * 137 _ Blank - Same
2223 * 140 ` Reserved - Humm.
2224 * 141 a Solid rectangle - Similar
2225 * 142 b 1/ - Top half of fraction for the
2226 * 143 c 3/ - subscript numbers below.
2229 * 146 f Degrees - Same
2230 * 147 g Plus or minus - Same
2232 * 151 i Ellipsis (dots)
2235 * 154 l Bar at scan 0
2236 * 155 m Bar at scan 1
2237 * 156 n Bar at scan 2
2238 * 157 o Bar at scan 3 - Similar
2239 * 160 p Bar at scan 4 - Similar
2240 * 161 q Bar at scan 5 - Similar
2241 * 162 r Bar at scan 6 - Same
2242 * 163 s Bar at scan 7 - Similar
2257 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2260 cset_attr
[cset
= 0] = ATTR_ASCII
;
2267 scroll(0, rows
- 1, -1, TRUE
);
2268 else if (curs
.y
> 0)
2274 erase_lots(FALSE
, FALSE
, TRUE
);
2278 erase_lots(TRUE
, FALSE
, TRUE
);
2282 /* XXX Print cursor line */
2285 /* XXX Start controller mode */
2288 /* XXX Stop controller mode */
2292 termstate
= VT52_Y1
;
2295 ldisc_send("\033/Z", 3);
2298 app_keypad_keys
= TRUE
;
2301 app_keypad_keys
= FALSE
;
2304 /* XXX This should switch to VT100 mode not current or default
2305 * VT mode. But this will only have effect in a VT220+
2309 blink_is_real
= cfg
.blinktext
;
2313 /* XXX Enter auto print mode */
2316 /* XXX Exit auto print mode */
2319 /* XXX Print screen */
2325 /* compatibility(ATARI) */
2327 erase_lots(FALSE
, FALSE
, TRUE
);
2331 /* compatibility(ATARI) */
2332 if (curs
.y
<= marg_b
)
2333 scroll(curs
.y
, marg_b
, -1, FALSE
);
2336 /* compatibility(ATARI) */
2337 if (curs
.y
<= marg_b
)
2338 scroll(curs
.y
, marg_b
, 1, TRUE
);
2341 /* compatibility(ATARI) */
2342 termstate
= VT52_FG
;
2345 /* compatibility(ATARI) */
2346 termstate
= VT52_BG
;
2349 /* compatibility(ATARI) */
2350 erase_lots(FALSE
, TRUE
, FALSE
);
2354 /* compatibility(ATARI) */
2358 /* compatibility(ATARI) */
2361 /* case 'j': Save cursor position - broken on ST */
2362 /* case 'k': Restore cursor position */
2364 /* compatibility(ATARI) */
2365 erase_lots(TRUE
, TRUE
, TRUE
);
2371 /* compatibility(ATARI) */
2372 erase_lots(TRUE
, TRUE
, FALSE
);
2375 /* compatibility(ATARI) */
2376 curr_attr
|= ATTR_REVERSE
;
2379 /* compatibility(ATARI) */
2380 curr_attr
&= ~ATTR_REVERSE
;
2382 case 'v': /* wrap Autowrap on - Wyse style */
2383 /* compatibility(ATARI) */
2386 case 'w': /* Autowrap off */
2387 /* compatibility(ATARI) */
2392 /* compatibility(OTHER) */
2394 curr_attr
= ATTR_DEFAULT
;
2396 erase_char
= (' ' | ATTR_ASCII
|
2398 (ATTR_FGMASK
| ATTR_BGMASK
)));
2401 /* compatibility(VI50) */
2402 curr_attr
|= ATTR_UNDER
;
2405 /* compatibility(VI50) */
2406 curr_attr
&= ~ATTR_UNDER
;
2409 /* compatibility(VI50) */
2411 curr_attr
|= ATTR_BOLD
;
2414 /* compatibility(VI50) */
2416 curr_attr
&= ~ATTR_BOLD
;
2422 termstate
= VT52_Y2
;
2423 move(curs
.x
, c
- ' ', 0);
2426 termstate
= TOPLEVEL
;
2427 move(c
- ' ', curs
.y
, 0);
2432 termstate
= TOPLEVEL
;
2433 curr_attr
&= ~ATTR_FGMASK
;
2434 curr_attr
&= ~ATTR_BOLD
;
2435 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2436 if ((c
& 0x8) || vt52_bold
)
2437 curr_attr
|= ATTR_BOLD
;
2440 erase_char
= (' ' | ATTR_ASCII
|
2441 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2444 termstate
= TOPLEVEL
;
2445 curr_attr
&= ~ATTR_BGMASK
;
2446 curr_attr
&= ~ATTR_BLINK
;
2447 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2449 /* Note: bold background */
2451 curr_attr
|= ATTR_BLINK
;
2454 erase_char
= (' ' | ATTR_ASCII
|
2455 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2458 default: break; /* placate gcc warning about enum use */
2460 if (selstate
!= NO_SELECTION
) {
2461 pos cursplus
= curs
;
2463 check_selection(curs
, cursplus
);
2471 * Compare two lines to determine whether they are sufficiently
2472 * alike to scroll-optimise one to the other. Return the degree of
2475 static int linecmp(unsigned long *a
, unsigned long *b
)
2479 for (i
= n
= 0; i
< cols
; i
++)
2480 n
+= (*a
++ == *b
++);
2486 * Given a context, update the window. Out of paranoia, we don't
2487 * allow WM_PAINT responses to do scrolling optimisations.
2489 static void do_paint(Context ctx
, int may_optimise
)
2491 int i
, j
, our_curs_y
;
2492 unsigned long rv
, cursor
;
2495 long cursor_background
= ERASE_CHAR
;
2499 * Check the visual bell state.
2502 ticks
= GetTickCount();
2503 if (ticks
- vbell_timeout
>= 0)
2507 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2510 * screen array, disptop, scrtop,
2512 * cfg.blinkpc, blink_is_real, tblinker,
2513 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2516 /* Has the cursor position or type changed ? */
2519 if (blinker
|| !cfg
.blink_cur
)
2520 cursor
= TATTR_ACTCURS
;
2524 cursor
= TATTR_PASCURS
;
2526 cursor
|= TATTR_RIGHTCURS
;
2529 our_curs_y
= curs
.y
- disptop
;
2531 if (dispcurs
&& (curstype
!= cursor
||
2533 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2534 if (dispcurs
> disptext
&& (dispcurs
[-1] & ATTR_WIDE
))
2535 dispcurs
[-1] |= ATTR_INVALID
;
2536 if ((*dispcurs
& ATTR_WIDE
))
2537 dispcurs
[1] |= ATTR_INVALID
;
2538 *dispcurs
|= ATTR_INVALID
;
2543 /* The normal screen data */
2544 for (i
= 0; i
< rows
; i
++) {
2545 unsigned long *ldata
;
2547 int idx
, dirty_line
, dirty_run
;
2548 unsigned long attr
= 0;
2549 int updated_line
= 0;
2552 int last_run_dirty
= 0;
2554 scrpos
.y
= i
+ disptop
;
2555 ldata
= lineptr(scrpos
.y
);
2556 lattr
= (ldata
[cols
] & LATTR_MODE
);
2558 idx
= i
* (cols
+ 1);
2559 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2560 disptext
[idx
+ cols
] = ldata
[cols
];
2562 for (j
= 0; j
< cols
; j
++, idx
++) {
2563 unsigned long tattr
, tchar
;
2564 unsigned long *d
= ldata
+ j
;
2568 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2569 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2570 switch (tchar
& CSET_MASK
) {
2572 tchar
= unitab_line
[tchar
& 0xFF];
2575 tchar
= unitab_xterm
[tchar
& 0xFF];
2578 tchar
= unitab_scoacs
[tchar
&0xFF];
2581 tattr
|= (tchar
& CSET_MASK
);
2584 /* Video reversing things */
2586 ^ (posle(selstart
, scrpos
) &&
2587 poslt(scrpos
, selend
) ? ATTR_REVERSE
: 0));
2589 /* 'Real' blinking ? */
2590 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2591 if (has_focus
&& tblinker
) {
2593 tattr
&= ~CSET_MASK
;
2596 tattr
&= ~ATTR_BLINK
;
2599 /* Cursor here ? Save the 'background' */
2600 if (i
== our_curs_y
&& j
== curs
.x
) {
2601 cursor_background
= tattr
| tchar
;
2602 dispcurs
= disptext
+ idx
;
2605 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2608 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2610 /* Special hack for VT100 Linedraw glyphs */
2611 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2612 && tchar
<= 0xBD) break_run
= TRUE
;
2614 if (!dbcs_screenfont
&& !dirty_line
) {
2615 if ((tchar
| tattr
) == disptext
[idx
])
2617 else if (!dirty_run
&& ccount
== 1)
2622 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2623 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2629 if (dbcs_screenfont
)
2630 last_run_dirty
= dirty_run
;
2631 dirty_run
= dirty_line
;
2634 if ((tchar
| tattr
) != disptext
[idx
])
2636 ch
[ccount
++] = (char) tchar
;
2637 disptext
[idx
] = tchar
| tattr
;
2639 /* If it's a wide char step along to the next one. */
2640 if (tattr
& ATTR_WIDE
) {
2644 /* Cursor is here ? Ouch! */
2645 if (i
== our_curs_y
&& j
== curs
.x
) {
2646 cursor_background
= *d
;
2647 dispcurs
= disptext
+ idx
;
2649 if (disptext
[idx
] != *d
)
2655 if (dirty_run
&& ccount
> 0) {
2656 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2660 /* Cursor on this line ? (and changed) */
2661 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2662 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2663 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2664 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2671 * Flick the switch that says if blinking things should be shown or hidden.
2674 void term_blink(int flg
)
2676 static long last_blink
= 0;
2677 static long last_tblink
= 0;
2678 long now
, blink_diff
;
2680 now
= GetTickCount();
2681 blink_diff
= now
- last_tblink
;
2683 /* Make sure the text blinks no more than 2Hz */
2684 if (blink_diff
< 0 || blink_diff
> 450) {
2686 tblinker
= !tblinker
;
2695 blink_diff
= now
- last_blink
;
2697 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2698 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2706 * Invalidate the whole screen so it will be repainted in full.
2708 void term_invalidate(void)
2712 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2713 disptext
[i
] = ATTR_INVALID
;
2717 * Paint the window in response to a WM_PAINT message.
2719 void term_paint(Context ctx
, int l
, int t
, int r
, int b
)
2721 int i
, j
, left
, top
, right
, bottom
;
2723 left
= l
/ font_width
;
2724 right
= (r
- 1) / font_width
;
2725 top
= t
/ font_height
;
2726 bottom
= (b
- 1) / font_height
;
2727 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2728 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2729 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2730 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2732 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2733 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2736 /* This should happen soon enough, also for some reason it sometimes
2737 * fails to actually do anything when re-sizing ... painting the wrong
2739 do_paint (ctx, FALSE);
2744 * Attempt to scroll the scrollback. The second parameter gives the
2745 * position we want to scroll to; the first is +1 to denote that
2746 * this position is relative to the beginning of the scrollback, -1
2747 * to denote it is relative to the end, and 0 to denote that it is
2748 * relative to the current position.
2750 void term_scroll(int rel
, int where
)
2752 int sbtop
= -count234(scrollback
);
2754 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2755 if (disptop
< sbtop
)
2763 static void clipme(pos top
, pos bottom
)
2766 wchar_t *wbptr
; /* where next char goes within workbuf */
2767 int wblen
= 0; /* workbuf len */
2768 int buflen
; /* amount of memory allocated to workbuf */
2770 buflen
= 5120; /* Default size */
2771 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2772 wbptr
= workbuf
; /* start filling here */
2774 while (poslt(top
, bottom
)) {
2776 unsigned long *ldata
= lineptr(top
.y
);
2782 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2783 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2784 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2785 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2786 && poslt(top
, nlpos
))
2788 if (poslt(nlpos
, bottom
))
2791 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2794 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2796 wchar_t cbuf
[16], *p
;
2797 int uc
= (ldata
[top
.x
] & 0xFFFF);
2800 if (uc
== UCSWIDE
) {
2805 switch (uc
& CSET_MASK
) {
2808 uc
= unitab_xterm
[uc
& 0xFF];
2812 uc
= unitab_line
[uc
& 0xFF];
2815 uc
= unitab_scoacs
[uc
&0xFF];
2818 switch (uc
& CSET_MASK
) {
2820 uc
= unitab_font
[uc
& 0xFF];
2823 uc
= unitab_oemcp
[uc
& 0xFF];
2827 set
= (uc
& CSET_MASK
);
2828 c
= (uc
& CHAR_MASK
);
2832 if (DIRECT_FONT(uc
)) {
2833 if (c
>= ' ' && c
!= 0x7F) {
2834 unsigned char buf
[4];
2837 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
2839 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
2840 rv
= MultiByteToWideChar(font_codepage
,
2841 0, buf
, 2, wbuf
, 4);
2845 rv
= MultiByteToWideChar(font_codepage
,
2846 0, buf
, 1, wbuf
, 4);
2850 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
2857 for (p
= cbuf
; *p
; p
++) {
2858 /* Enough overhead for trailing NL and nul */
2859 if (wblen
>= buflen
- 16) {
2862 sizeof(wchar_t) * (buflen
+= 100));
2863 wbptr
= workbuf
+ wblen
;
2872 for (i
= 0; i
< sel_nl_sz
; i
++) {
2874 *wbptr
++ = sel_nl
[i
];
2882 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
2883 if (buflen
> 0) /* indicates we allocated this buffer */
2887 void term_copyall(void)
2890 top
.y
= -count234(scrollback
);
2896 * The wordness array is mainly for deciding the disposition of the US-ASCII
2899 static int wordtype(int uc
)
2902 int start
, end
, ctype
;
2903 } *wptr
, ucs_words
[] = {
2909 0x037e, 0x037e, 1}, /* Greek question mark */
2911 0x0387, 0x0387, 1}, /* Greek ano teleia */
2913 0x055a, 0x055f, 1}, /* Armenian punctuation */
2915 0x0589, 0x0589, 1}, /* Armenian full stop */
2917 0x0700, 0x070d, 1}, /* Syriac punctuation */
2919 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2921 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2923 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2925 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2927 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2929 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2931 0x2000, 0x200a, 0}, /* Various spaces */
2933 0x2070, 0x207f, 2}, /* superscript */
2935 0x2080, 0x208f, 2}, /* subscript */
2937 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2939 0x3000, 0x3000, 0}, /* ideographic space */
2941 0x3001, 0x3020, 1}, /* ideographic punctuation */
2943 0x303f, 0x309f, 3}, /* Hiragana */
2945 0x30a0, 0x30ff, 3}, /* Katakana */
2947 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2949 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2951 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2953 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2955 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2957 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2959 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2961 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2963 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2968 uc
&= (CSET_MASK
| CHAR_MASK
);
2970 switch (uc
& CSET_MASK
) {
2972 uc
= unitab_xterm
[uc
& 0xFF];
2975 uc
= unitab_line
[uc
& 0xFF];
2978 uc
= unitab_scoacs
[uc
&0xFF];
2981 switch (uc
& CSET_MASK
) {
2983 uc
= unitab_font
[uc
& 0xFF];
2986 uc
= unitab_oemcp
[uc
& 0xFF];
2991 return wordness
[uc
];
2993 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
2994 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
3002 * Spread the selection outwards according to the selection mode.
3004 static pos
sel_spread_half(pos p
, int dir
)
3006 unsigned long *ldata
;
3009 ldata
= lineptr(p
.y
);
3014 * In this mode, every character is a separate unit, except
3015 * for runs of spaces at the end of a non-wrapping line.
3017 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
3018 unsigned long *q
= ldata
+ cols
;
3019 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
3021 if (q
== ldata
+ cols
)
3023 if (p
.x
>= q
- ldata
)
3024 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
3029 * In this mode, the units are maximal runs of characters
3030 * whose `wordness' has the same value.
3032 wvalue
= wordtype(ldata
[p
.x
]);
3034 while (p
.x
< cols
&& wordtype(ldata
[p
.x
+ 1]) == wvalue
)
3037 while (p
.x
> 0 && wordtype(ldata
[p
.x
- 1]) == wvalue
)
3043 * In this mode, every line is a unit.
3045 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3051 static void sel_spread(void)
3053 selstart
= sel_spread_half(selstart
, -1);
3055 selend
= sel_spread_half(selend
, +1);
3059 void term_do_paste(void)
3064 get_clip(&data
, &len
);
3069 sfree(paste_buffer
);
3070 paste_pos
= paste_hold
= paste_len
= 0;
3071 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3074 while (p
< data
+ len
) {
3075 while (p
< data
+ len
&&
3076 !(p
<= data
+ len
- sel_nl_sz
&&
3077 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3082 for (i
= 0; i
< p
- q
; i
++) {
3083 paste_buffer
[paste_len
++] = q
[i
];
3087 if (p
<= data
+ len
- sel_nl_sz
&&
3088 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3089 paste_buffer
[paste_len
++] = '\r';
3095 /* Assume a small paste will be OK in one go. */
3096 if (paste_len
< 256) {
3097 luni_send(paste_buffer
, paste_len
);
3099 sfree(paste_buffer
);
3101 paste_pos
= paste_hold
= paste_len
= 0;
3104 get_clip(NULL
, NULL
);
3107 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3108 int shift
, int ctrl
)
3111 unsigned long *ldata
;
3112 int raw_mouse
= xterm_mouse
&& !(cfg
.mouse_override
&& shift
);
3116 if (a
== MA_DRAG
&& !raw_mouse
)
3121 if (a
== MA_DRAG
&& !raw_mouse
)
3134 selpoint
.y
= y
+ disptop
;
3136 ldata
= lineptr(selpoint
.y
);
3137 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3141 int encstate
= 0, r
, c
;
3143 static int is_down
= 0;
3147 encstate
= 0x20; /* left button down */
3158 case MBT_WHEEL_DOWN
:
3161 default: break; /* placate gcc warning about enum use */
3165 if (xterm_mouse
== 1)
3178 default: break; /* placate gcc warning about enum use */
3187 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3188 ldisc_send(abuf
, 6);
3192 b
= translate_button(b
);
3194 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3196 selstate
= ABOUT_TO
;
3197 selanchor
= selpoint
;
3199 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3201 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3202 selstate
= DRAGGING
;
3203 selstart
= selanchor
= selpoint
;
3207 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3208 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3209 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3211 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3212 if (posdiff(selpoint
, selstart
) <
3213 posdiff(selend
, selstart
) / 2) {
3217 selanchor
= selstart
;
3219 selstate
= DRAGGING
;
3221 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3222 selanchor
= selpoint
;
3223 selstate
= DRAGGING
;
3224 if (poslt(selpoint
, selanchor
)) {
3225 selstart
= selpoint
;
3229 selstart
= selanchor
;
3234 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3235 if (selstate
== DRAGGING
) {
3237 * We've completed a selection. We now transfer the
3238 * data to the clipboard.
3240 clipme(selstart
, selend
);
3241 selstate
= SELECTED
;
3243 selstate
= NO_SELECTION
;
3244 } else if (b
== MBT_PASTE
3245 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3256 sfree(paste_buffer
);
3263 static long last_paste
= 0;
3264 long now
, paste_diff
;
3269 /* Don't wait forever to paste */
3271 now
= GetTickCount();
3272 paste_diff
= now
- last_paste
;
3273 if (paste_diff
>= 0 && paste_diff
< 450)
3278 while (paste_pos
< paste_len
) {
3280 while (n
+ paste_pos
< paste_len
) {
3281 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3284 luni_send(paste_buffer
+ paste_pos
, n
);
3287 if (paste_pos
< paste_len
) {
3292 sfree(paste_buffer
);
3297 static void deselect(void)
3299 selstate
= NO_SELECTION
;
3300 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3303 void term_deselect(void)
3309 int term_ldisc(int option
)
3311 if (option
== LD_ECHO
)
3312 return term_echoing
;
3313 if (option
== LD_EDIT
)
3314 return term_editing
;
3319 * from_backend(), to get data from the backend for the terminal.
3321 int from_backend(int is_stderr
, char *data
, int len
)
3324 if (inbuf_head
>= INBUF_SIZE
)
3326 inbuf
[inbuf_head
++] = *data
++;
3330 * We process all stdout/stderr data immediately we receive it,
3331 * and don't return until it's all gone. Therefore, there's no
3332 * reason at all to return anything other than zero from this
3335 * This is a slightly suboptimal way to deal with SSH2 - in
3336 * principle, the window mechanism would allow us to continue
3337 * to accept data on forwarded ports and X connections even
3338 * while the terminal processing was going slowly - but we
3339 * can't do the 100% right thing without moving the terminal
3340 * processing into a separate thread, and that might hurt
3341 * portability. So we manage stdout buffering the old SSH1 way:
3342 * if the terminal processing goes slowly, the whole SSH
3343 * connection stops accepting data until it's ready.
3345 * In practice, I can't imagine this causing serious trouble.
3351 * Log session traffic.
3353 void logtraffic(unsigned char c
, int logmode
)
3355 if (cfg
.logtype
> 0) {
3356 if (cfg
.logtype
== logmode
) {
3357 /* deferred open file from pgm start? */
3366 void settimstr(char *ta
, int no_sec
);
3367 char *subslfcode(char *dest
, char *src
, char *dstrt
);
3368 char *stpncpy(char *dst
, const char *src
, size_t maxlen
);
3370 char currlogfilename
[FILENAME_MAX
];
3372 /* open log file append/overwrite mode */
3382 sprintf(writemod
, "wb"); /* default to rewrite */
3385 tm
= *localtime(&t
);
3387 /* substitute special codes in file name */
3388 xlatlognam(currlogfilename
,cfg
.logfilename
,cfg
.host
, &tm
);
3390 lgfp
= fopen(currlogfilename
, "r"); /* file already present? */
3394 i
= askappend(currlogfilename
);
3396 writemod
[0] = 'a'; /* set append mode */
3397 else if (i
== 0) { /* cancelled */
3399 cfg
.logtype
= 0; /* disable logging */
3404 lgfp
= fopen(currlogfilename
, writemod
);
3405 if (lgfp
) { /* enter into event log */
3406 sprintf(buf
, "%s session log (%s mode) to file : ",
3407 (writemod
[0] == 'a') ?
"Appending" : "Writing new",
3408 (cfg
.logtype
== LGTYP_ASCII ?
"ASCII" :
3409 cfg
.logtype
== LGTYP_DEBUG ?
"raw" : "<ukwn>"));
3410 /* Make sure we do not exceed the output buffer size */
3411 strncat(buf
, currlogfilename
, 128);
3412 buf
[strlen(buf
)] = '\0';
3415 /* --- write header line into log file */
3416 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp
);
3417 strftime(buf
, 24, "%Y.%m.%d %H:%M:%S", &tm
);
3419 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp
);
3423 void logfclose(void)
3432 * translate format codes into time/date strings
3433 * and insert them into log file name
3435 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3437 static void xlatlognam(char *d
, char *s
, char *hostname
, struct tm
*tm
) {
3438 char buf
[10], *bufp
;
3440 char *ds
= d
; /* save start pos. */
3441 int len
= FILENAME_MAX
-1;
3444 /* Let (bufp, len) be the string to append. */
3445 bufp
= buf
; /* don't usually override this */
3449 if (*s
) switch (c
= *s
++, tolower(c
)) {
3451 size
= strftime(buf
, sizeof(buf
), "%Y", tm
);
3454 size
= strftime(buf
, sizeof(buf
), "%m", tm
);
3457 size
= strftime(buf
, sizeof(buf
), "%d", tm
);
3460 size
= strftime(buf
, sizeof(buf
), "%H%M%S", tm
);
3464 size
= strlen(bufp
);
3478 memcpy(d
, bufp
, size
);