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 rvideo
; /* global reverse video flag */
98 static int rvbell_timeout
; /* for ESC[?5hESC[?5l vbell */
99 static int cursor_on
; /* cursor enabled flag */
100 static int reset_132
; /* Flag ESC c resets to 80 cols */
101 static int use_bce
; /* Use Background coloured erase */
102 static int blinker
; /* When blinking is the cursor on ? */
103 static int tblinker
; /* When the blinking text is on */
104 static int blink_is_real
; /* Actually blink blinking text */
105 static int term_echoing
; /* Does terminal want local echo? */
106 static int term_editing
; /* Does terminal want local edit? */
107 static int sco_acs
, save_sco_acs
; /* CSI 10,11,12m -> OEM charset */
108 static int vt52_bold
; /* Force bold on non-bold colours */
109 static int utf_state
; /* Is there a pending UTF-8 character */
110 static int utf_char
; /* and what is it so far. */
111 static int utf_size
; /* The size of the UTF character. */
113 static int xterm_mouse
; /* send mouse messages to app */
115 static unsigned long cset_attr
[2];
118 * Saved settings on the alternate screen.
120 static int alt_x
, alt_y
, alt_om
, alt_wrap
, alt_wnext
, alt_ins
, alt_cset
, alt_sco_acs
;
121 static int alt_t
, alt_b
;
122 static int alt_which
;
124 #define ARGS_MAX 32 /* max # of esc sequence arguments */
125 #define ARG_DEFAULT 0 /* if an arg isn't specified */
126 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
127 static int esc_args
[ARGS_MAX
];
128 static int esc_nargs
;
129 static int esc_query
;
130 #define ANSI(x,y) ((x)+((y)<<8))
131 #define ANSI_QUE(x) ANSI(x,TRUE)
133 #define OSC_STR_MAX 2048
134 static int osc_strlen
;
135 static char osc_string
[OSC_STR_MAX
+ 1];
138 static char id_string
[1024] = "\033[?6c";
140 static unsigned char *tabs
;
152 OSC_STRING
, OSC_MAYBE_ST
,
161 NO_SELECTION
, ABOUT_TO
, DRAGGING
, SELECTED
164 SM_CHAR
, SM_WORD
, SM_LINE
166 static pos selstart
, selend
, selanchor
;
168 static short wordness
[256] = {
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
171 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
172 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
173 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
174 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
175 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
176 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
177 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
180 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
181 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
182 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
183 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
184 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
187 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
188 static wchar_t sel_nl
[] = SEL_NL
;
189 static wchar_t *paste_buffer
= 0;
190 static int paste_len
, paste_pos
, paste_hold
;
193 * Internal prototypes.
195 static void do_paint(Context
, int);
196 static void erase_lots(int, int, int);
197 static void swap_screen(int);
198 static void update_sbar(void);
199 static void deselect(void);
200 /* log session to file stuff ... */
201 static FILE *lgfp
= NULL
;
202 static void logtraffic(unsigned char c
, int logmode
);
205 * Resize a line to make it `cols' columns wide.
207 unsigned long *resizeline(unsigned long *line
, int cols
)
210 unsigned long lineattrs
;
212 if (line
[0] != (unsigned long)cols
) {
214 * This line is the wrong length, which probably means it
215 * hasn't been accessed since a resize. Resize it now.
218 lineattrs
= line
[oldlen
+ 1];
219 line
= srealloc(line
, TSIZE
* (2 + cols
));
221 for (i
= oldlen
; i
< cols
; i
++)
222 line
[i
+ 1] = ERASE_CHAR
;
223 line
[cols
+ 1] = lineattrs
& LATTR_MODE
;
230 * Retrieve a line of the screen or of the scrollback, according to
231 * whether the y coordinate is non-negative or negative
234 unsigned long *lineptr(int y
, int lineno
)
236 unsigned long *line
, *newline
;
244 whichtree
= scrollback
;
245 treeindex
= y
+ count234(scrollback
);
247 line
= index234(whichtree
, treeindex
);
249 /* We assume that we don't screw up and retrieve something out of range. */
250 assert(line
!= NULL
);
252 newline
= resizeline(line
, cols
);
253 if (newline
!= line
) {
254 delpos234(whichtree
, treeindex
);
255 addpos234(whichtree
, newline
, treeindex
);
261 #define lineptr(x) lineptr(x,__LINE__)
263 * Set up power-on settings for the terminal.
265 static void power_on(void)
267 curs
.x
= curs
.y
= alt_x
= alt_y
= savecurs
.x
= savecurs
.y
= 0;
270 alt_b
= marg_b
= rows
- 1;
275 for (i
= 0; i
< cols
; i
++)
276 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
278 alt_om
= dec_om
= cfg
.dec_om
;
279 alt_wnext
= wrapnext
= alt_ins
= insert
= FALSE
;
280 alt_wrap
= wrap
= cfg
.wrap_mode
;
282 alt_sco_acs
= sco_acs
= 0;
283 cset_attr
[0] = cset_attr
[1] = ATTR_ASCII
;
288 save_attr
= curr_attr
= ATTR_DEFAULT
;
289 term_editing
= term_echoing
= FALSE
;
290 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
291 app_cursor_keys
= cfg
.app_cursor
;
292 app_keypad_keys
= cfg
.app_keypad
;
294 blink_is_real
= cfg
.blinktext
;
295 erase_char
= ERASE_CHAR
;
299 for (i
= 0; i
< 256; i
++)
300 wordness
[i
] = cfg
.wordness
[i
];
304 erase_lots(FALSE
, TRUE
, TRUE
);
306 erase_lots(FALSE
, TRUE
, TRUE
);
311 * Force a screen update.
313 void term_update(void)
320 if ((seen_key_event
&& (cfg
.scroll_on_key
)) ||
321 (seen_disp_event
&& (cfg
.scroll_on_disp
))) {
322 disptop
= 0; /* return to main screen */
323 seen_disp_event
= seen_key_event
= 0;
326 sys_cursor(curs
.x
, curs
.y
- disptop
);
332 * Same as power_on(), but an external function.
334 void term_pwron(void)
344 * Clear the scrollback.
346 void term_clrsb(void)
350 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
357 * Initialise the terminal.
361 screen
= alt_screen
= scrollback
= NULL
;
363 disptext
= dispcurs
= NULL
;
368 beephead
= beeptail
= NULL
;
371 beep_overloaded
= FALSE
;
375 * Set up the terminal for a given size.
377 void term_size(int newrows
, int newcols
, int newsavelines
)
380 unsigned long *newdisp
, *line
;
383 int save_alt_which
= alt_which
;
385 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
386 return; /* nothing to do */
392 alt_b
= marg_b
= newrows
- 1;
395 scrollback
= newtree234(NULL
);
396 screen
= newtree234(NULL
);
401 * Resize the screen and scrollback. We only need to shift
402 * lines around within our data structures, because lineptr()
403 * will take care of resizing each individual line if
406 * - If the new screen and the old screen differ in length, we
407 * must shunt some lines in from the scrollback or out to
410 * - If doing that fails to provide us with enough material to
411 * fill the new screen (i.e. the number of rows needed in
412 * the new screen exceeds the total number in the previous
413 * screen+scrollback), we must invent some blank lines to
416 * - Then, if the new scrollback length is less than the
417 * amount of scrollback we actually have, we must throw some
420 sblen
= count234(scrollback
);
421 /* Do this loop to expand the screen if newrows > rows */
422 for (i
= rows
; i
< newrows
; i
++) {
424 line
= delpos234(scrollback
, --sblen
);
426 line
= smalloc(TSIZE
* (newcols
+ 2));
428 for (j
= 0; j
<= newcols
; j
++)
429 line
[j
+ 1] = ERASE_CHAR
;
431 addpos234(screen
, line
, 0);
433 /* Do this loop to shrink the screen if newrows < rows */
434 for (i
= newrows
; i
< rows
; i
++) {
435 line
= delpos234(screen
, 0);
436 addpos234(scrollback
, line
, sblen
++);
438 assert(count234(screen
) == newrows
);
439 while (sblen
> newsavelines
) {
440 line
= delpos234(scrollback
, 0);
444 assert(count234(scrollback
) <= newsavelines
);
447 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
448 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
449 newdisp
[i
] = ATTR_INVALID
;
454 newalt
= newtree234(NULL
);
455 for (i
= 0; i
< newrows
; i
++) {
456 line
= smalloc(TSIZE
* (newcols
+ 2));
458 for (j
= 0; j
<= newcols
; j
++)
459 line
[j
+ 1] = erase_char
;
460 addpos234(newalt
, line
, i
);
463 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
465 freetree234(alt_screen
);
469 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
472 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
473 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
477 curs
.y
+= newrows
- rows
;
480 if (curs
.y
>= newrows
)
481 curs
.y
= newrows
- 1;
482 if (curs
.x
>= newcols
)
483 curs
.x
= newcols
- 1;
485 wrapnext
= alt_wnext
= FALSE
;
489 savelines
= newsavelines
;
492 swap_screen(save_alt_which
);
501 static void swap_screen(int which
)
506 if (which
== alt_which
)
533 wrapnext
= alt_wnext
;
542 sco_acs
= alt_sco_acs
;
549 * Update the scroll bar.
551 static void update_sbar(void)
555 nscroll
= count234(scrollback
);
557 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
561 * Check whether the region bounded by the two pointers intersects
562 * the scroll region, and de-select the on-screen selection if so.
564 static void check_selection(pos from
, pos to
)
566 if (poslt(from
, selend
) && poslt(selstart
, to
))
571 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
572 * for backward.) `sb' is TRUE if the scrolling is permitted to
573 * affect the scrollback buffer.
575 * NB this function invalidates all pointers into lines of the
576 * screen data structures. In particular, you MUST call fix_cpos
577 * after calling scroll() and before doing anything else that
578 * uses the cpos shortcut pointer.
580 static void scroll(int topline
, int botline
, int lines
, int sb
)
582 unsigned long *line
, *line2
;
585 if (topline
!= 0 || alt_which
!= 0)
590 line
= delpos234(screen
, botline
);
591 line
= resizeline(line
, cols
);
592 for (i
= 0; i
< cols
; i
++)
593 line
[i
+ 1] = erase_char
;
595 addpos234(screen
, line
, topline
);
597 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
599 if (selstart
.y
> botline
) {
600 selstart
.y
= botline
;
604 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
606 if (selend
.y
> botline
) {
616 line
= delpos234(screen
, topline
);
617 if (sb
&& savelines
> 0) {
618 int sblen
= count234(scrollback
);
620 * We must add this line to the scrollback. We'll
621 * remove a line from the top of the scrollback to
622 * replace it, or allocate a new one if the
623 * scrollback isn't full.
625 if (sblen
== savelines
) {
626 sblen
--, line2
= delpos234(scrollback
, 0);
628 line2
= smalloc(TSIZE
* (cols
+ 2));
631 addpos234(scrollback
, line
, sblen
);
634 line
= resizeline(line
, cols
);
635 for (i
= 0; i
< cols
; i
++)
636 line
[i
+ 1] = erase_char
;
638 addpos234(screen
, line
, botline
);
640 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
642 if (selstart
.y
< topline
) {
643 selstart
.y
= topline
;
647 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
649 if (selend
.y
< topline
) {
661 * Move the cursor to a given position, clipping at boundaries. We
662 * may or may not want to clip at the scroll margin: marg_clip is 0
663 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
664 * even _being_ outside the margins.
666 static void move(int x
, int y
, int marg_clip
)
673 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
675 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
689 * Save or restore the cursor and SGR mode.
691 static void save_cursor(int save
)
695 save_attr
= curr_attr
;
697 save_csattr
= cset_attr
[cset
];
698 save_sco_acs
= sco_acs
;
701 /* Make sure the window hasn't shrunk since the save */
707 curr_attr
= save_attr
;
709 cset_attr
[cset
] = save_csattr
;
710 sco_acs
= save_sco_acs
;
713 erase_char
= (' ' | (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
718 * Erase a large portion of the screen: the whole screen, or the
719 * whole line, or parts thereof.
721 static void erase_lots(int line_only
, int from_begin
, int to_end
)
725 unsigned long *ldata
;
747 check_selection(start
, end
);
749 /* Clear screen also forces a full window redraw, just in case. */
750 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
753 ldata
= lineptr(start
.y
);
754 while (poslt(start
, end
)) {
755 if (start
.x
== cols
&& !erase_lattr
)
756 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
758 ldata
[start
.x
] = erase_char
;
759 if (incpos(start
) && start
.y
< rows
)
760 ldata
= lineptr(start
.y
);
765 * Insert or delete characters within the current line. n is +ve if
766 * insertion is desired, and -ve for deletion.
768 static void insch(int n
)
770 int dir
= (n
< 0 ?
-1 : +1);
773 unsigned long *ldata
;
775 n
= (n
< 0 ?
-n
: n
);
776 if (n
> cols
- curs
.x
)
778 m
= cols
- curs
.x
- n
;
780 cursplus
.x
= curs
.x
+ n
;
781 check_selection(curs
, cursplus
);
782 ldata
= lineptr(curs
.y
);
784 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
786 ldata
[curs
.x
+ m
++] = erase_char
;
788 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
790 ldata
[curs
.x
+ n
] = erase_char
;
795 * Toggle terminal mode `mode' to state `state'. (`query' indicates
796 * whether the mode is a DEC private one or a normal one.)
798 static void toggle_mode(int mode
, int query
, int state
)
804 case 1: /* application cursor keys */
805 app_cursor_keys
= state
;
807 case 2: /* VT52 mode */
810 blink_is_real
= FALSE
;
813 blink_is_real
= cfg
.blinktext
;
816 case 3: /* 80/132 columns */
818 request_resize(state ?
132 : 80, rows
, 1);
821 case 5: /* reverse video */
823 * Toggle reverse video. If we receive an OFF within the
824 * visual bell timeout period after an ON, we trigger an
825 * effective visual bell, so that ESC[?5hESC[?5l will
826 * always be an actually _visible_ visual bell.
828 ticks
= GetTickCount();
829 if (rvideo
&& !state
&& /* we're turning it off */
830 ticks
< rvbell_timeout
) { /* and it's not long since it was turned on */
831 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
832 if (vbell_timeout
< rvbell_timeout
) /* don't move vbell end forward */
833 vbell_timeout
= rvbell_timeout
; /* vbell end is at least then */
834 } else if (!rvideo
&& state
) {
835 /* This is an ON, so we notice the time and save it. */
836 rvbell_timeout
= ticks
+ VBELL_TIMEOUT
;
839 seen_disp_event
= TRUE
;
843 case 6: /* DEC origin mode */
846 case 7: /* auto wrap */
849 case 8: /* auto key repeat */
852 case 10: /* set local edit mode */
853 term_editing
= state
;
854 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
856 case 25: /* enable/disable cursor */
857 compatibility2(OTHER
, VT220
);
859 seen_disp_event
= TRUE
;
861 case 47: /* alternate screen */
862 compatibility(OTHER
);
867 case 1000: /* xterm mouse 1 */
868 xterm_mouse
= state ?
1 : 0;
869 set_raw_mouse_mode(state
);
871 case 1002: /* xterm mouse 2 */
872 xterm_mouse
= state ?
2 : 0;
873 set_raw_mouse_mode(state
);
877 case 4: /* set insert mode */
878 compatibility(VT102
);
881 case 12: /* set echo mode */
882 term_echoing
= !state
;
883 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
885 case 20: /* Return sends ... */
886 cr_lf_return
= state
;
888 case 34: /* Make cursor BIG */
889 compatibility2(OTHER
, VT220
);
895 * Process an OSC sequence: set window title or icon name.
897 static void do_osc(void)
901 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
903 osc_string
[osc_strlen
] = '\0';
904 switch (esc_args
[0]) {
907 set_icon(osc_string
);
908 if (esc_args
[0] == 1)
910 /* fall through: parameter 0 means set both */
913 set_title(osc_string
);
920 * Remove everything currently in `inbuf' and stick it up on the
921 * in-memory display. There's a big state machine in here to
922 * process escape sequences...
928 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
929 c
= inbuf
[inbuf_reap
];
932 * Optionally log the session traffic to a file. Useful for
933 * debugging and possibly also useful for actual logging.
935 logtraffic((unsigned char) c
, LGTYP_DEBUG
);
937 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
938 * be able to display 8-bit characters, but I'll let that go 'cause
942 /* First see about all those translations. */
943 if (termstate
== TOPLEVEL
) {
948 /* I know; gotos are evil. This one is really bad!
949 * But before you try removing it follow the path of the
950 * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
953 if (cfg.no_vt_graph_with_utf8) break;
956 } else if ((c
& 0xe0) == 0xc0) {
957 utf_size
= utf_state
= 1;
958 utf_char
= (c
& 0x1f);
959 } else if ((c
& 0xf0) == 0xe0) {
960 utf_size
= utf_state
= 2;
961 utf_char
= (c
& 0x0f);
962 } else if ((c
& 0xf8) == 0xf0) {
963 utf_size
= utf_state
= 3;
964 utf_char
= (c
& 0x07);
965 } else if ((c
& 0xfc) == 0xf8) {
966 utf_size
= utf_state
= 4;
967 utf_char
= (c
& 0x03);
968 } else if ((c
& 0xfe) == 0xfc) {
969 utf_size
= utf_state
= 5;
970 utf_char
= (c
& 0x01);
981 if ((c
& 0xC0) != 0x80) {
982 inbuf_reap
--; /* This causes the faulting character */
983 c
= UCSERR
; /* to be logged twice - not really a */
984 utf_state
= 0; /* serious problem. */
987 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
993 /* Is somebody trying to be evil! */
995 (c
< 0x800 && utf_size
>= 2) ||
996 (c
< 0x10000 && utf_size
>= 3) ||
997 (c
< 0x200000 && utf_size
>= 4) ||
998 (c
< 0x4000000 && utf_size
>= 5))
1001 /* Unicode line separator and paragraph separator are CR-LF */
1002 if (c
== 0x2028 || c
== 0x2029)
1005 /* High controls are probably a Baaad idea too. */
1009 /* The UTF-16 surrogates are not nice either. */
1010 /* The standard give the option of decoding these:
1011 * I don't want to! */
1012 if (c
>= 0xD800 && c
< 0xE000)
1015 /* ISO 10646 characters now limited to UTF-16 range. */
1019 /* This is currently a TagPhobic application.. */
1020 if (c
>= 0xE0000 && c
<= 0xE007F)
1023 /* U+FEFF is best seen as a null. */
1026 /* But U+FFFE is an error. */
1027 if (c
== 0xFFFE || c
== 0xFFFF)
1030 /* Oops this is a 16bit implementation */
1035 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1037 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1039 if (sco_acs
== 2) c
^= 0x80;
1043 switch (cset_attr
[cset
]) {
1045 * Linedraw characters are different from 'ESC ( B'
1046 * only for a small range. For ones outside that
1047 * range, make sure we use the same font as well as
1048 * the same encoding.
1051 if (unitab_ctrl
[c
] != 0xFF)
1054 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1058 /* If UK-ASCII, make the '#' a LineDraw Pound */
1060 c
= '}' | ATTR_LINEDRW
;
1063 /*FALLTHROUGH*/ case ATTR_ASCII
:
1064 if (unitab_ctrl
[c
] != 0xFF)
1067 c
= ((unsigned char) c
) | ATTR_ASCII
;
1070 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1076 /* How about C1 controls ? */
1077 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1078 has_compat(VT220
)) {
1079 termstate
= SEEN_ESC
;
1081 c
= '@' + (c
& 0x1F);
1084 /* Or the GL control. */
1085 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1086 if (curs
.x
&& !wrapnext
)
1090 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1092 /* Or normal C0 controls. */
1093 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1095 case '\005': /* terminal type query */
1096 /* Strictly speaking this is VT100 but a VT100 defaults to
1097 * no response. Other terminals respond at their option.
1099 * Don't put a CR in the default string as this tends to
1100 * upset some weird software.
1102 * An xterm returns "xterm" (5 characters)
1104 compatibility(ANSIMIN
);
1106 char abuf
[256], *s
, *d
;
1108 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1110 if (*s
>= 'a' && *s
<= 'z')
1111 *d
++ = (*s
- ('a' - 1));
1112 else if ((*s
>= '@' && *s
<= '_') ||
1113 *s
== '?' || (*s
& 0x80))
1118 } else if (*s
== '^') {
1123 lpage_send(CP_ACP
, abuf
, d
- abuf
);
1128 struct beeptime
*newbeep
;
1131 ticks
= GetTickCount();
1133 if (!beep_overloaded
) {
1134 newbeep
= smalloc(sizeof(struct beeptime
));
1135 newbeep
->ticks
= ticks
;
1136 newbeep
->next
= NULL
;
1140 beeptail
->next
= newbeep
;
1146 * Throw out any beeps that happened more than
1150 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1151 struct beeptime
*tmp
= beephead
;
1152 beephead
= tmp
->next
;
1159 if (cfg
.bellovl
&& beep_overloaded
&&
1160 ticks
- lastbeep
>= cfg
.bellovl_s
) {
1162 * If we're currently overloaded and the
1163 * last beep was more than s seconds ago,
1164 * leave overload mode.
1166 beep_overloaded
= FALSE
;
1167 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1168 nbeeps
>= cfg
.bellovl_n
) {
1170 * Now, if we have n or more beeps
1171 * remaining in the queue, go into overload
1174 beep_overloaded
= TRUE
;
1179 * Perform an actual beep if we're not overloaded.
1181 if (!cfg
.bellovl
|| !beep_overloaded
) {
1183 if (cfg
.beep
== BELL_VISUAL
) {
1185 vbell_timeout
= ticks
+ VBELL_TIMEOUT
;
1193 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1194 else if (curs
.x
== 0 && curs
.y
> 0)
1195 curs
.x
= cols
- 1, curs
.y
--;
1201 seen_disp_event
= TRUE
;
1204 compatibility(VT100
);
1208 compatibility(VT100
);
1213 termstate
= VT52_ESC
;
1215 compatibility(ANSIMIN
);
1216 termstate
= SEEN_ESC
;
1224 seen_disp_event
= TRUE
;
1226 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1229 if (has_compat(SCOANSI
)) {
1231 erase_lots(FALSE
, FALSE
, TRUE
);
1234 seen_disp_event
= 1;
1238 compatibility(VT100
);
1240 if (curs
.y
== marg_b
)
1241 scroll(marg_t
, marg_b
, 1, TRUE
);
1242 else if (curs
.y
< rows
- 1)
1248 seen_disp_event
= 1;
1250 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1254 pos old_curs
= curs
;
1255 unsigned long *ldata
= lineptr(curs
.y
);
1259 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1261 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1262 if (curs
.x
>= cols
/ 2)
1263 curs
.x
= cols
/ 2 - 1;
1270 check_selection(old_curs
, curs
);
1272 seen_disp_event
= TRUE
;
1276 switch (termstate
) {
1278 /* Only graphic characters get this far, ctrls are stripped above */
1279 if (wrapnext
&& wrap
) {
1280 cpos
[1] |= LATTR_WRAPPED
;
1281 if (curs
.y
== marg_b
)
1282 scroll(marg_t
, marg_b
, 1, TRUE
);
1283 else if (curs
.y
< rows
- 1)
1291 if (selstate
!= NO_SELECTION
) {
1292 pos cursplus
= curs
;
1294 check_selection(curs
, cursplus
);
1296 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1297 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1299 extern int wcwidth(wchar_t ucs
);
1304 width
= wcwidth((wchar_t) c
);
1307 if (curs
.x
+ 1 != cols
) {
1308 *cpos
++ = c
| ATTR_WIDE
| curr_attr
;
1309 *cpos
++ = UCSWIDE
| curr_attr
;
1314 *cpos
++ = c
| curr_attr
;
1321 if (curs
.x
== cols
) {
1325 if (wrap
&& vt52_mode
) {
1326 cpos
[1] |= LATTR_WRAPPED
;
1327 if (curs
.y
== marg_b
)
1328 scroll(marg_t
, marg_b
, 1, TRUE
);
1329 else if (curs
.y
< rows
- 1)
1336 seen_disp_event
= 1;
1341 * This state is virtually identical to SEEN_ESC, with the
1342 * exception that we have an OSC sequence in the pipeline,
1343 * and _if_ we see a backslash, we process it.
1347 termstate
= TOPLEVEL
;
1350 /* else fall through */
1352 if (c
>= ' ' && c
<= '/') {
1359 termstate
= TOPLEVEL
;
1360 switch (ANSI(c
, esc_query
)) {
1361 case '[': /* enter CSI mode */
1362 termstate
= SEEN_CSI
;
1364 esc_args
[0] = ARG_DEFAULT
;
1367 case ']': /* xterm escape sequences */
1368 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1369 compatibility(OTHER
);
1370 termstate
= SEEN_OSC
;
1373 case '7': /* save cursor */
1374 compatibility(VT100
);
1377 case '8': /* restore cursor */
1378 compatibility(VT100
);
1380 seen_disp_event
= TRUE
;
1383 compatibility(VT100
);
1384 app_keypad_keys
= TRUE
;
1387 compatibility(VT100
);
1388 app_keypad_keys
= FALSE
;
1390 case 'D': /* exactly equivalent to LF */
1391 compatibility(VT100
);
1392 if (curs
.y
== marg_b
)
1393 scroll(marg_t
, marg_b
, 1, TRUE
);
1394 else if (curs
.y
< rows
- 1)
1398 seen_disp_event
= TRUE
;
1400 case 'E': /* exactly equivalent to CR-LF */
1401 compatibility(VT100
);
1403 if (curs
.y
== marg_b
)
1404 scroll(marg_t
, marg_b
, 1, TRUE
);
1405 else if (curs
.y
< rows
- 1)
1409 seen_disp_event
= TRUE
;
1411 case 'M': /* reverse index - backwards LF */
1412 compatibility(VT100
);
1413 if (curs
.y
== marg_t
)
1414 scroll(marg_t
, marg_b
, -1, TRUE
);
1415 else if (curs
.y
> 0)
1419 seen_disp_event
= TRUE
;
1421 case 'Z': /* terminal type query */
1422 compatibility(VT100
);
1423 ldisc_send(id_string
, strlen(id_string
));
1425 case 'c': /* restore power-on settings */
1426 compatibility(VT100
);
1429 request_resize(80, rows
, 1);
1434 seen_disp_event
= TRUE
;
1436 case 'H': /* set a tab */
1437 compatibility(VT100
);
1438 tabs
[curs
.x
] = TRUE
;
1441 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1442 compatibility(VT100
);
1444 unsigned long *ldata
;
1448 for (i
= 0; i
< rows
; i
++) {
1450 for (j
= 0; j
< cols
; j
++)
1451 ldata
[j
] = ATTR_DEFAULT
| 'E';
1455 seen_disp_event
= TRUE
;
1456 scrtop
.x
= scrtop
.y
= 0;
1459 check_selection(scrtop
, scrbot
);
1463 case ANSI('3', '#'):
1464 case ANSI('4', '#'):
1465 case ANSI('5', '#'):
1466 case ANSI('6', '#'):
1467 compatibility(VT100
);
1469 unsigned long nlattr
;
1470 unsigned long *ldata
;
1471 switch (ANSI(c
, esc_query
)) {
1472 case ANSI('3', '#'):
1475 case ANSI('4', '#'):
1478 case ANSI('5', '#'):
1479 nlattr
= LATTR_NORM
;
1481 default: /* spiritually case ANSI('6', '#'): */
1482 nlattr
= LATTR_WIDE
;
1485 ldata
= lineptr(curs
.y
);
1486 ldata
[cols
] &= ~LATTR_MODE
;
1487 ldata
[cols
] |= nlattr
;
1491 case ANSI('A', '('):
1492 compatibility(VT100
);
1493 cset_attr
[0] = ATTR_GBCHR
;
1495 case ANSI('B', '('):
1496 compatibility(VT100
);
1497 cset_attr
[0] = ATTR_ASCII
;
1499 case ANSI('0', '('):
1500 compatibility(VT100
);
1501 cset_attr
[0] = ATTR_LINEDRW
;
1503 case ANSI('U', '('):
1504 compatibility(OTHER
);
1505 cset_attr
[0] = ATTR_SCOACS
;
1508 case ANSI('A', ')'):
1509 compatibility(VT100
);
1510 cset_attr
[1] = ATTR_GBCHR
;
1512 case ANSI('B', ')'):
1513 compatibility(VT100
);
1514 cset_attr
[1] = ATTR_ASCII
;
1516 case ANSI('0', ')'):
1517 compatibility(VT100
);
1518 cset_attr
[1] = ATTR_LINEDRW
;
1520 case ANSI('U', ')'):
1521 compatibility(OTHER
);
1522 cset_attr
[1] = ATTR_SCOACS
;
1525 case ANSI('8', '%'): /* Old Linux code */
1526 case ANSI('G', '%'):
1527 compatibility(OTHER
);
1530 case ANSI('@', '%'):
1531 compatibility(OTHER
);
1532 if (line_codepage
!= CP_UTF8
)
1538 termstate
= TOPLEVEL
; /* default */
1540 if (esc_nargs
<= ARGS_MAX
) {
1541 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1542 esc_args
[esc_nargs
- 1] = 0;
1543 esc_args
[esc_nargs
- 1] =
1544 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1546 termstate
= SEEN_CSI
;
1547 } else if (c
== ';') {
1548 if (++esc_nargs
<= ARGS_MAX
)
1549 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1550 termstate
= SEEN_CSI
;
1551 } else if (c
< '@') {
1558 termstate
= SEEN_CSI
;
1560 switch (ANSI(c
, esc_query
)) {
1561 case 'A': /* move up N lines */
1562 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1563 seen_disp_event
= TRUE
;
1565 case 'e': /* move down N lines */
1566 compatibility(ANSI
);
1569 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1570 seen_disp_event
= TRUE
;
1572 case ANSI('c', '>'): /* report xterm version */
1573 compatibility(OTHER
);
1574 /* this reports xterm version 136 so that VIM can
1575 use the drag messages from the mouse reporting */
1576 ldisc_send("\033[>0;136;0c", 11);
1578 case 'a': /* move right N cols */
1579 compatibility(ANSI
);
1582 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1583 seen_disp_event
= TRUE
;
1585 case 'D': /* move left N cols */
1586 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1587 seen_disp_event
= TRUE
;
1589 case 'E': /* move down N lines and CR */
1590 compatibility(ANSI
);
1591 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1592 seen_disp_event
= TRUE
;
1594 case 'F': /* move up N lines and CR */
1595 compatibility(ANSI
);
1596 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1597 seen_disp_event
= TRUE
;
1600 case '`': /* set horizontal posn */
1601 compatibility(ANSI
);
1602 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1603 seen_disp_event
= TRUE
;
1605 case 'd': /* set vertical posn */
1606 compatibility(ANSI
);
1608 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1611 seen_disp_event
= TRUE
;
1614 case 'f': /* set horz and vert posns at once */
1616 esc_args
[1] = ARG_DEFAULT
;
1617 move(def(esc_args
[1], 1) - 1,
1618 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1621 seen_disp_event
= TRUE
;
1623 case 'J': /* erase screen or parts of it */
1625 unsigned int i
= def(esc_args
[0], 0) + 1;
1628 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1631 seen_disp_event
= TRUE
;
1633 case 'K': /* erase line or parts of it */
1635 unsigned int i
= def(esc_args
[0], 0) + 1;
1638 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1640 seen_disp_event
= TRUE
;
1642 case 'L': /* insert lines */
1643 compatibility(VT102
);
1644 if (curs
.y
<= marg_b
)
1645 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1648 seen_disp_event
= TRUE
;
1650 case 'M': /* delete lines */
1651 compatibility(VT102
);
1652 if (curs
.y
<= marg_b
)
1653 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1656 seen_disp_event
= TRUE
;
1658 case '@': /* insert chars */
1659 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1660 compatibility(VT102
);
1661 insch(def(esc_args
[0], 1));
1662 seen_disp_event
= TRUE
;
1664 case 'P': /* delete chars */
1665 compatibility(VT102
);
1666 insch(-def(esc_args
[0], 1));
1667 seen_disp_event
= TRUE
;
1669 case 'c': /* terminal type query */
1670 compatibility(VT100
);
1671 /* This is the response for a VT102 */
1672 ldisc_send(id_string
, strlen(id_string
));
1674 case 'n': /* cursor position query */
1675 if (esc_args
[0] == 6) {
1677 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1679 ldisc_send(buf
, strlen(buf
));
1680 } else if (esc_args
[0] == 5) {
1681 ldisc_send("\033[0n", 4);
1684 case 'h': /* toggle modes to high */
1686 compatibility(VT100
);
1689 for (i
= 0; i
< esc_nargs
; i
++)
1690 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1693 case 'l': /* toggle modes to low */
1695 compatibility(VT100
);
1698 for (i
= 0; i
< esc_nargs
; i
++)
1699 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1702 case 'g': /* clear tabs */
1703 compatibility(VT100
);
1704 if (esc_nargs
== 1) {
1705 if (esc_args
[0] == 0) {
1706 tabs
[curs
.x
] = FALSE
;
1707 } else if (esc_args
[0] == 3) {
1709 for (i
= 0; i
< cols
; i
++)
1714 case 'r': /* set scroll margins */
1715 compatibility(VT100
);
1716 if (esc_nargs
<= 2) {
1718 top
= def(esc_args
[0], 1) - 1;
1719 bot
= (esc_nargs
<= 1
1721 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1724 /* VTTEST Bug 9 - if region is less than 2 lines
1725 * don't change region.
1727 if (bot
- top
> 0) {
1732 * I used to think the cursor should be
1733 * placed at the top of the newly marginned
1734 * area. Apparently not: VMS TPU falls over
1737 * Well actually it should for Origin mode - RDB
1739 curs
.y
= (dec_om ? marg_t
: 0);
1741 seen_disp_event
= TRUE
;
1745 case 'm': /* set graphics rendition */
1748 * A VT100 without the AVO only had one attribute, either
1749 * underline or reverse video depending on the cursor type,
1750 * this was selected by CSI 7m.
1753 * This is sometimes DIM, eg on the GIGI and Linux
1755 * This is sometimes INVIS various ANSI.
1757 * This like 22 disables BOLD, DIM and INVIS
1759 * The ANSI colours appear on any terminal that has colour
1760 * (obviously) but the interaction between sgr0 and the
1761 * colours varies but is usually related to the background
1762 * colour erase item.
1763 * The interaction between colour attributes and the mono
1764 * ones is also very implementation dependent.
1766 * The 39 and 49 attributes are likely to be unimplemented.
1769 for (i
= 0; i
< esc_nargs
; i
++) {
1770 switch (def(esc_args
[i
], 0)) {
1771 case 0: /* restore defaults */
1772 curr_attr
= ATTR_DEFAULT
;
1774 case 1: /* enable bold */
1775 compatibility(VT100AVO
);
1776 curr_attr
|= ATTR_BOLD
;
1778 case 21: /* (enable double underline) */
1779 compatibility(OTHER
);
1780 case 4: /* enable underline */
1781 compatibility(VT100AVO
);
1782 curr_attr
|= ATTR_UNDER
;
1784 case 5: /* enable blink */
1785 compatibility(VT100AVO
);
1786 curr_attr
|= ATTR_BLINK
;
1788 case 7: /* enable reverse video */
1789 curr_attr
|= ATTR_REVERSE
;
1791 case 10: /* SCO acs off */
1792 compatibility(SCOANSI
);
1794 case 11: /* SCO acs on */
1795 compatibility(SCOANSI
);
1797 case 12: /* SCO acs on flipped */
1798 compatibility(SCOANSI
);
1800 case 22: /* disable bold */
1801 compatibility2(OTHER
, VT220
);
1802 curr_attr
&= ~ATTR_BOLD
;
1804 case 24: /* disable underline */
1805 compatibility2(OTHER
, VT220
);
1806 curr_attr
&= ~ATTR_UNDER
;
1808 case 25: /* disable blink */
1809 compatibility2(OTHER
, VT220
);
1810 curr_attr
&= ~ATTR_BLINK
;
1812 case 27: /* disable reverse video */
1813 compatibility2(OTHER
, VT220
);
1814 curr_attr
&= ~ATTR_REVERSE
;
1825 curr_attr
&= ~ATTR_FGMASK
;
1827 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1829 case 39: /* default-foreground */
1830 curr_attr
&= ~ATTR_FGMASK
;
1831 curr_attr
|= ATTR_DEFFG
;
1842 curr_attr
&= ~ATTR_BGMASK
;
1844 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1846 case 49: /* default-background */
1847 curr_attr
&= ~ATTR_BGMASK
;
1848 curr_attr
|= ATTR_DEFBG
;
1856 (ATTR_FGMASK
| ATTR_BGMASK
|
1860 case 's': /* save cursor */
1863 case 'u': /* restore cursor */
1865 seen_disp_event
= TRUE
;
1867 case 't': /* set page size - ie window height */
1869 * VT340/VT420 sequence DECSLPP, DEC only allows values
1870 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1871 * illegal values (eg first arg 1..9) for window changing
1874 compatibility(VT340TEXT
);
1876 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1877 request_resize(cols
, def(esc_args
[0], 24), 0);
1882 compatibility(SCOANSI
);
1883 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
1886 seen_disp_event
= TRUE
;
1889 compatibility(SCOANSI
);
1890 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
1893 seen_disp_event
= TRUE
;
1895 case ANSI('|', '*'):
1896 /* VT420 sequence DECSNLS
1897 * Set number of lines on screen
1898 * VT420 uses VGA like hardware and can support any size in
1899 * reasonable range (24..49 AIUI) with no default specified.
1901 compatibility(VT420
);
1902 if (esc_nargs
== 1 && esc_args
[0] > 0) {
1903 request_resize(cols
,
1904 def(esc_args
[0], cfg
.height
),
1909 case ANSI('|', '$'):
1910 /* VT340/VT420 sequence DECSCPP
1911 * Set number of columns per page
1912 * Docs imply range is only 80 or 132, but I'll allow any.
1914 compatibility(VT340TEXT
);
1915 if (esc_nargs
<= 1) {
1916 request_resize(def(esc_args
[0], cfg
.width
),
1921 case 'X': /* write N spaces w/o moving cursor */
1922 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1923 compatibility(ANSIMIN
);
1925 int n
= def(esc_args
[0], 1);
1927 unsigned long *p
= cpos
;
1928 if (n
> cols
- curs
.x
)
1932 check_selection(curs
, cursplus
);
1935 seen_disp_event
= TRUE
;
1938 case 'x': /* report terminal characteristics */
1939 compatibility(VT100
);
1942 int i
= def(esc_args
[0], 0);
1943 if (i
== 0 || i
== 1) {
1944 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
1946 ldisc_send(buf
, 20);
1950 case ANSI('L', '='):
1951 compatibility(OTHER
);
1952 use_bce
= (esc_args
[0] <= 0);
1953 erase_char
= ERASE_CHAR
;
1958 (ATTR_FGMASK
| ATTR_BGMASK
)));
1960 case ANSI('E', '='):
1961 compatibility(OTHER
);
1962 blink_is_real
= (esc_args
[0] >= 1);
1964 case ANSI('p', '"'):
1965 /* Allow the host to make this emulator a 'perfect' VT102.
1966 * This first appeared in the VT220, but we do need to get
1967 * back to PuTTY mode so I won't check it.
1969 * The arg in 40..42,50 are a PuTTY extension.
1970 * The 2nd arg, 8bit vs 7bit is not checked.
1972 * Setting VT102 mode should also change the Fkeys to
1973 * generate PF* codes as a real VT102 has no Fkeys.
1974 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1977 * Note ESC c will NOT change this!
1980 switch (esc_args
[0]) {
1982 compatibility_level
&= ~TM_VTXXX
;
1983 compatibility_level
|= TM_VT102
;
1986 compatibility_level
&= ~TM_VTXXX
;
1987 compatibility_level
|= TM_VT220
;
1991 if (esc_args
[0] > 60 && esc_args
[0] < 70)
1992 compatibility_level
|= TM_VTXXX
;
1996 compatibility_level
&= TM_VTXXX
;
1999 compatibility_level
= TM_PUTTY
;
2002 compatibility_level
= TM_SCOANSI
;
2006 compatibility_level
= TM_PUTTY
;
2012 /* Change the response to CSI c */
2013 if (esc_args
[0] == 50) {
2016 strcpy(id_string
, "\033[?");
2017 for (i
= 1; i
< esc_nargs
; i
++) {
2019 strcat(id_string
, ";");
2020 sprintf(lbuf
, "%d", esc_args
[i
]);
2021 strcat(id_string
, lbuf
);
2023 strcat(id_string
, "c");
2026 /* Is this a good idea ?
2027 * Well we should do a soft reset at this point ...
2029 if (!has_compat(VT420
) && has_compat(VT100
)) {
2031 request_resize(132, 24, 1);
2033 request_resize(80, 24, 1);
2042 case 'P': /* Linux palette sequence */
2043 termstate
= SEEN_OSC_P
;
2046 case 'R': /* Linux palette reset */
2049 termstate
= TOPLEVEL
;
2051 case 'W': /* word-set */
2052 termstate
= SEEN_OSC_W
;
2065 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2069 * Grotty hack to support xterm and DECterm title
2070 * sequences concurrently.
2072 if (esc_args
[0] == 2) {
2076 /* else fall through */
2078 termstate
= OSC_STRING
;
2084 * This OSC stuff is EVIL. It takes just one character to get into
2085 * sysline mode and it's not initially obvious how to get out.
2086 * So I've added CR and LF as string aborts.
2087 * This shouldn't effect compatibility as I believe embedded
2088 * control characters are supposed to be interpreted (maybe?)
2089 * and they don't display anything useful anyway.
2093 if (c
== '\n' || c
== '\r') {
2094 termstate
= TOPLEVEL
;
2095 } else if (c
== 0234 || c
== '\007') {
2097 * These characters terminate the string; ST and BEL
2098 * terminate the sequence and trigger instant
2099 * processing of it, whereas ESC goes back to SEEN_ESC
2100 * mode unless it is followed by \, in which case it is
2101 * synonymous with ST in the first place.
2104 termstate
= TOPLEVEL
;
2105 } else if (c
== '\033')
2106 termstate
= OSC_MAYBE_ST
;
2107 else if (osc_strlen
< OSC_STR_MAX
)
2108 osc_string
[osc_strlen
++] = c
;
2112 int max
= (osc_strlen
== 0 ?
21 : 16);
2114 if (c
>= '0' && c
<= '9')
2116 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2118 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2121 termstate
= TOPLEVEL
;
2124 osc_string
[osc_strlen
++] = val
;
2125 if (osc_strlen
>= 7) {
2126 palette_set(osc_string
[0],
2127 osc_string
[1] * 16 + osc_string
[2],
2128 osc_string
[3] * 16 + osc_string
[4],
2129 osc_string
[5] * 16 + osc_string
[6]);
2131 termstate
= TOPLEVEL
;
2147 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2150 termstate
= OSC_STRING
;
2155 termstate
= TOPLEVEL
;
2156 seen_disp_event
= TRUE
;
2159 move(curs
.x
, curs
.y
- 1, 1);
2162 move(curs
.x
, curs
.y
+ 1, 1);
2165 move(curs
.x
+ 1, curs
.y
, 1);
2168 move(curs
.x
- 1, curs
.y
, 1);
2171 * From the VT100 Manual
2172 * NOTE: The special graphics characters in the VT100
2173 * are different from those in the VT52
2175 * From VT102 manual:
2176 * 137 _ Blank - Same
2177 * 140 ` Reserved - Humm.
2178 * 141 a Solid rectangle - Similar
2179 * 142 b 1/ - Top half of fraction for the
2180 * 143 c 3/ - subscript numbers below.
2183 * 146 f Degrees - Same
2184 * 147 g Plus or minus - Same
2186 * 151 i Ellipsis (dots)
2189 * 154 l Bar at scan 0
2190 * 155 m Bar at scan 1
2191 * 156 n Bar at scan 2
2192 * 157 o Bar at scan 3 - Similar
2193 * 160 p Bar at scan 4 - Similar
2194 * 161 q Bar at scan 5 - Similar
2195 * 162 r Bar at scan 6 - Same
2196 * 163 s Bar at scan 7 - Similar
2211 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2214 cset_attr
[cset
= 0] = ATTR_ASCII
;
2221 scroll(0, rows
- 1, -1, TRUE
);
2222 else if (curs
.y
> 0)
2228 erase_lots(FALSE
, FALSE
, TRUE
);
2232 erase_lots(TRUE
, FALSE
, TRUE
);
2236 /* XXX Print cursor line */
2239 /* XXX Start controller mode */
2242 /* XXX Stop controller mode */
2246 termstate
= VT52_Y1
;
2249 ldisc_send("\033/Z", 3);
2252 app_keypad_keys
= TRUE
;
2255 app_keypad_keys
= FALSE
;
2258 /* XXX This should switch to VT100 mode not current or default
2259 * VT mode. But this will only have effect in a VT220+
2263 blink_is_real
= cfg
.blinktext
;
2267 /* XXX Enter auto print mode */
2270 /* XXX Exit auto print mode */
2273 /* XXX Print screen */
2279 /* compatibility(ATARI) */
2281 erase_lots(FALSE
, FALSE
, TRUE
);
2285 /* compatibility(ATARI) */
2286 if (curs
.y
<= marg_b
)
2287 scroll(curs
.y
, marg_b
, -1, FALSE
);
2290 /* compatibility(ATARI) */
2291 if (curs
.y
<= marg_b
)
2292 scroll(curs
.y
, marg_b
, 1, TRUE
);
2295 /* compatibility(ATARI) */
2296 termstate
= VT52_FG
;
2299 /* compatibility(ATARI) */
2300 termstate
= VT52_BG
;
2303 /* compatibility(ATARI) */
2304 erase_lots(FALSE
, TRUE
, FALSE
);
2308 /* compatibility(ATARI) */
2312 /* compatibility(ATARI) */
2315 /* case 'j': Save cursor position - broken on ST */
2316 /* case 'k': Restore cursor position */
2318 /* compatibility(ATARI) */
2319 erase_lots(TRUE
, TRUE
, TRUE
);
2325 /* compatibility(ATARI) */
2326 erase_lots(TRUE
, TRUE
, FALSE
);
2329 /* compatibility(ATARI) */
2330 curr_attr
|= ATTR_REVERSE
;
2333 /* compatibility(ATARI) */
2334 curr_attr
&= ~ATTR_REVERSE
;
2336 case 'v': /* wrap Autowrap on - Wyse style */
2337 /* compatibility(ATARI) */
2340 case 'w': /* Autowrap off */
2341 /* compatibility(ATARI) */
2346 /* compatibility(OTHER) */
2348 curr_attr
= ATTR_DEFAULT
;
2352 (ATTR_FGMASK
| ATTR_BGMASK
|
2356 /* compatibility(VI50) */
2357 curr_attr
|= ATTR_UNDER
;
2360 /* compatibility(VI50) */
2361 curr_attr
&= ~ATTR_UNDER
;
2364 /* compatibility(VI50) */
2366 curr_attr
|= ATTR_BOLD
;
2369 /* compatibility(VI50) */
2371 curr_attr
&= ~ATTR_BOLD
;
2377 termstate
= VT52_Y2
;
2378 move(curs
.x
, c
- ' ', 0);
2381 termstate
= TOPLEVEL
;
2382 move(c
- ' ', curs
.y
, 0);
2387 termstate
= TOPLEVEL
;
2388 curr_attr
&= ~ATTR_FGMASK
;
2389 curr_attr
&= ~ATTR_BOLD
;
2390 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2391 if ((c
& 0x8) || vt52_bold
)
2392 curr_attr
|= ATTR_BOLD
;
2397 (ATTR_FGMASK
| ATTR_BGMASK
|
2401 termstate
= TOPLEVEL
;
2402 curr_attr
&= ~ATTR_BGMASK
;
2403 curr_attr
&= ~ATTR_BLINK
;
2404 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2406 /* Note: bold background */
2408 curr_attr
|= ATTR_BLINK
;
2413 (ATTR_FGMASK
| ATTR_BGMASK
|
2417 default: break; /* placate gcc warning about enum use */
2419 if (selstate
!= NO_SELECTION
) {
2420 pos cursplus
= curs
;
2422 check_selection(curs
, cursplus
);
2430 * Compare two lines to determine whether they are sufficiently
2431 * alike to scroll-optimise one to the other. Return the degree of
2434 static int linecmp(unsigned long *a
, unsigned long *b
)
2438 for (i
= n
= 0; i
< cols
; i
++)
2439 n
+= (*a
++ == *b
++);
2445 * Given a context, update the window. Out of paranoia, we don't
2446 * allow WM_PAINT responses to do scrolling optimisations.
2448 static void do_paint(Context ctx
, int may_optimise
)
2450 int i
, j
, our_curs_y
;
2451 unsigned long rv
, cursor
;
2454 long cursor_background
= ERASE_CHAR
;
2458 * Check the visual bell state.
2461 ticks
= GetTickCount();
2462 if (ticks
- vbell_timeout
>= 0)
2466 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2469 * screen array, disptop, scrtop,
2471 * cfg.blinkpc, blink_is_real, tblinker,
2472 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2475 /* Has the cursor position or type changed ? */
2478 if (blinker
|| !cfg
.blink_cur
)
2479 cursor
= TATTR_ACTCURS
;
2483 cursor
= TATTR_PASCURS
;
2485 cursor
|= TATTR_RIGHTCURS
;
2488 our_curs_y
= curs
.y
- disptop
;
2490 if (dispcurs
&& (curstype
!= cursor
||
2492 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2493 if (dispcurs
> disptext
&& (dispcurs
[-1] & ATTR_WIDE
))
2494 dispcurs
[-1] |= ATTR_INVALID
;
2495 if ((*dispcurs
& ATTR_WIDE
))
2496 dispcurs
[1] |= ATTR_INVALID
;
2497 *dispcurs
|= ATTR_INVALID
;
2502 /* The normal screen data */
2503 for (i
= 0; i
< rows
; i
++) {
2504 unsigned long *ldata
;
2506 int idx
, dirty_line
, dirty_run
;
2507 unsigned long attr
= 0;
2508 int updated_line
= 0;
2511 int last_run_dirty
= 0;
2513 scrpos
.y
= i
+ disptop
;
2514 ldata
= lineptr(scrpos
.y
);
2515 lattr
= (ldata
[cols
] & LATTR_MODE
);
2517 idx
= i
* (cols
+ 1);
2518 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2519 disptext
[idx
+ cols
] = ldata
[cols
];
2521 for (j
= 0; j
< cols
; j
++, idx
++) {
2522 unsigned long tattr
, tchar
;
2523 unsigned long *d
= ldata
+ j
;
2527 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2528 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2529 switch (tchar
& CSET_MASK
) {
2531 tchar
= unitab_line
[tchar
& 0xFF];
2534 tchar
= unitab_xterm
[tchar
& 0xFF];
2537 tchar
= unitab_scoacs
[tchar
&0xFF];
2540 tattr
|= (tchar
& CSET_MASK
);
2543 /* Video reversing things */
2545 ^ (posle(selstart
, scrpos
) &&
2546 poslt(scrpos
, selend
) ? ATTR_REVERSE
: 0));
2548 /* 'Real' blinking ? */
2549 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2550 if (has_focus
&& tblinker
) {
2552 tattr
&= ~CSET_MASK
;
2555 tattr
&= ~ATTR_BLINK
;
2558 /* Cursor here ? Save the 'background' */
2559 if (i
== our_curs_y
&& j
== curs
.x
) {
2560 cursor_background
= tattr
| tchar
;
2561 dispcurs
= disptext
+ idx
;
2564 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2567 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2569 /* Special hack for VT100 Linedraw glyphs */
2570 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2571 && tchar
<= 0xBD) break_run
= TRUE
;
2573 if (!dbcs_screenfont
&& !dirty_line
) {
2574 if ((tchar
| tattr
) == disptext
[idx
])
2576 else if (!dirty_run
&& ccount
== 1)
2581 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2582 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2588 if (dbcs_screenfont
)
2589 last_run_dirty
= dirty_run
;
2590 dirty_run
= dirty_line
;
2593 if ((tchar
| tattr
) != disptext
[idx
])
2595 ch
[ccount
++] = (char) tchar
;
2596 disptext
[idx
] = tchar
| tattr
;
2598 /* If it's a wide char step along to the next one. */
2599 if (tattr
& ATTR_WIDE
) {
2603 /* Cursor is here ? Ouch! */
2604 if (i
== our_curs_y
&& j
== curs
.x
) {
2605 cursor_background
= *d
;
2606 dispcurs
= disptext
+ idx
;
2608 if (disptext
[idx
] != *d
)
2614 if (dirty_run
&& ccount
> 0) {
2615 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2619 /* Cursor on this line ? (and changed) */
2620 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2621 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2622 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2623 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2630 * Flick the switch that says if blinking things should be shown or hidden.
2633 void term_blink(int flg
)
2635 static long last_blink
= 0;
2636 static long last_tblink
= 0;
2637 long now
, blink_diff
;
2639 now
= GetTickCount();
2640 blink_diff
= now
- last_tblink
;
2642 /* Make sure the text blinks no more than 2Hz */
2643 if (blink_diff
< 0 || blink_diff
> 450) {
2645 tblinker
= !tblinker
;
2654 blink_diff
= now
- last_blink
;
2656 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2657 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2665 * Invalidate the whole screen so it will be repainted in full.
2667 void term_invalidate(void)
2671 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2672 disptext
[i
] = ATTR_INVALID
;
2676 * Paint the window in response to a WM_PAINT message.
2678 void term_paint(Context ctx
, int l
, int t
, int r
, int b
)
2680 int i
, j
, left
, top
, right
, bottom
;
2682 left
= l
/ font_width
;
2683 right
= (r
- 1) / font_width
;
2684 top
= t
/ font_height
;
2685 bottom
= (b
- 1) / font_height
;
2686 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2687 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2688 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2689 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2691 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2692 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2695 /* This should happen soon enough, also for some reason it sometimes
2696 * fails to actually do anything when re-sizing ... painting the wrong
2698 do_paint (ctx, FALSE);
2703 * Attempt to scroll the scrollback. The second parameter gives the
2704 * position we want to scroll to; the first is +1 to denote that
2705 * this position is relative to the beginning of the scrollback, -1
2706 * to denote it is relative to the end, and 0 to denote that it is
2707 * relative to the current position.
2709 void term_scroll(int rel
, int where
)
2711 int sbtop
= -count234(scrollback
);
2713 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2714 if (disptop
< sbtop
)
2722 static void clipme(pos top
, pos bottom
)
2725 wchar_t *wbptr
; /* where next char goes within workbuf */
2726 int wblen
= 0; /* workbuf len */
2727 int buflen
; /* amount of memory allocated to workbuf */
2729 buflen
= 5120; /* Default size */
2730 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2731 wbptr
= workbuf
; /* start filling here */
2733 while (poslt(top
, bottom
)) {
2735 unsigned long *ldata
= lineptr(top
.y
);
2741 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2742 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2743 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2744 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2745 && poslt(top
, nlpos
))
2747 if (poslt(nlpos
, bottom
))
2750 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2753 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2755 wchar_t cbuf
[16], *p
;
2756 int uc
= (ldata
[top
.x
] & 0xFFFF);
2759 if (uc
== UCSWIDE
) {
2764 switch (uc
& CSET_MASK
) {
2767 uc
= unitab_xterm
[uc
& 0xFF];
2771 uc
= unitab_line
[uc
& 0xFF];
2774 uc
= unitab_scoacs
[uc
&0xFF];
2777 switch (uc
& CSET_MASK
) {
2779 uc
= unitab_font
[uc
& 0xFF];
2782 uc
= unitab_oemcp
[uc
& 0xFF];
2786 set
= (uc
& CSET_MASK
);
2787 c
= (uc
& CHAR_MASK
);
2791 if (DIRECT_FONT(uc
)) {
2792 if (c
>= ' ' && c
!= 0x7F) {
2793 unsigned char buf
[4];
2796 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
2798 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
2799 rv
= MultiByteToWideChar(font_codepage
,
2800 0, buf
, 2, wbuf
, 4);
2804 rv
= MultiByteToWideChar(font_codepage
,
2805 0, buf
, 1, wbuf
, 4);
2809 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
2816 for (p
= cbuf
; *p
; p
++) {
2817 /* Enough overhead for trailing NL and nul */
2818 if (wblen
>= buflen
- 16) {
2821 sizeof(wchar_t) * (buflen
+= 100));
2822 wbptr
= workbuf
+ wblen
;
2831 for (i
= 0; i
< sel_nl_sz
; i
++) {
2833 *wbptr
++ = sel_nl
[i
];
2841 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
2842 if (buflen
> 0) /* indicates we allocated this buffer */
2846 void term_copyall(void)
2849 top
.y
= -count234(scrollback
);
2855 * The wordness array is mainly for deciding the disposition of the US-ASCII
2858 static int wordtype(int uc
)
2861 int start
, end
, ctype
;
2862 } *wptr
, ucs_words
[] = {
2868 0x037e, 0x037e, 1}, /* Greek question mark */
2870 0x0387, 0x0387, 1}, /* Greek ano teleia */
2872 0x055a, 0x055f, 1}, /* Armenian punctuation */
2874 0x0589, 0x0589, 1}, /* Armenian full stop */
2876 0x0700, 0x070d, 1}, /* Syriac punctuation */
2878 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2880 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2882 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2884 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2886 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2888 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2890 0x2000, 0x200a, 0}, /* Various spaces */
2892 0x2070, 0x207f, 2}, /* superscript */
2894 0x2080, 0x208f, 2}, /* subscript */
2896 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2898 0x3000, 0x3000, 0}, /* ideographic space */
2900 0x3001, 0x3020, 1}, /* ideographic punctuation */
2902 0x303f, 0x309f, 3}, /* Hiragana */
2904 0x30a0, 0x30ff, 3}, /* Katakana */
2906 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2908 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2910 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2912 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2914 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2916 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2918 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2920 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2922 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2927 uc
&= (CSET_MASK
| CHAR_MASK
);
2929 switch (uc
& CSET_MASK
) {
2931 uc
= unitab_xterm
[uc
& 0xFF];
2934 uc
= unitab_line
[uc
& 0xFF];
2937 uc
= unitab_scoacs
[uc
&0xFF];
2940 switch (uc
& CSET_MASK
) {
2942 uc
= unitab_font
[uc
& 0xFF];
2945 uc
= unitab_oemcp
[uc
& 0xFF];
2950 return wordness
[uc
];
2952 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
2953 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
2961 * Spread the selection outwards according to the selection mode.
2963 static pos
sel_spread_half(pos p
, int dir
)
2965 unsigned long *ldata
;
2968 ldata
= lineptr(p
.y
);
2973 * In this mode, every character is a separate unit, except
2974 * for runs of spaces at the end of a non-wrapping line.
2976 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2977 unsigned long *q
= ldata
+ cols
;
2978 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
2980 if (q
== ldata
+ cols
)
2982 if (p
.x
>= q
- ldata
)
2983 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
2988 * In this mode, the units are maximal runs of characters
2989 * whose `wordness' has the same value.
2991 wvalue
= wordtype(ldata
[p
.x
]);
2993 while (p
.x
< cols
&& wordtype(ldata
[p
.x
+ 1]) == wvalue
)
2996 while (p
.x
> 0 && wordtype(ldata
[p
.x
- 1]) == wvalue
)
3002 * In this mode, every line is a unit.
3004 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3010 static void sel_spread(void)
3012 selstart
= sel_spread_half(selstart
, -1);
3014 selend
= sel_spread_half(selend
, +1);
3018 void term_do_paste(void)
3023 get_clip(&data
, &len
);
3028 sfree(paste_buffer
);
3029 paste_pos
= paste_hold
= paste_len
= 0;
3030 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3033 while (p
< data
+ len
) {
3034 while (p
< data
+ len
&&
3035 !(p
<= data
+ len
- sel_nl_sz
&&
3036 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3041 for (i
= 0; i
< p
- q
; i
++) {
3042 paste_buffer
[paste_len
++] = q
[i
];
3046 if (p
<= data
+ len
- sel_nl_sz
&&
3047 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3048 paste_buffer
[paste_len
++] = '\r';
3054 /* Assume a small paste will be OK in one go. */
3055 if (paste_len
< 256) {
3056 luni_send(paste_buffer
, paste_len
);
3058 sfree(paste_buffer
);
3060 paste_pos
= paste_hold
= paste_len
= 0;
3063 get_clip(NULL
, NULL
);
3066 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3067 int shift
, int ctrl
)
3070 unsigned long *ldata
;
3086 selpoint
.y
= y
+ disptop
;
3088 ldata
= lineptr(selpoint
.y
);
3089 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3093 int encstate
= 0, r
, c
;
3095 static int is_down
= 0;
3099 encstate
= 0x20; /* left button down */
3110 case MBT_WHEEL_DOWN
:
3113 default: break; /* placate gcc warning about enum use */
3117 if (xterm_mouse
== 1)
3130 default: break; /* placate gcc warning about enum use */
3139 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3140 ldisc_send(abuf
, 6);
3144 b
= translate_button(b
);
3146 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3148 selstate
= ABOUT_TO
;
3149 selanchor
= selpoint
;
3151 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3153 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3154 selstate
= DRAGGING
;
3155 selstart
= selanchor
= selpoint
;
3159 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3160 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3161 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3163 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3164 if (posdiff(selpoint
, selstart
) <
3165 posdiff(selend
, selstart
) / 2) {
3169 selanchor
= selstart
;
3171 selstate
= DRAGGING
;
3173 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3174 selanchor
= selpoint
;
3175 selstate
= DRAGGING
;
3176 if (poslt(selpoint
, selanchor
)) {
3177 selstart
= selpoint
;
3181 selstart
= selanchor
;
3186 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3187 if (selstate
== DRAGGING
) {
3189 * We've completed a selection. We now transfer the
3190 * data to the clipboard.
3192 clipme(selstart
, selend
);
3193 selstate
= SELECTED
;
3195 selstate
= NO_SELECTION
;
3196 } else if (b
== MBT_PASTE
3197 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3208 sfree(paste_buffer
);
3215 static long last_paste
= 0;
3216 long now
, paste_diff
;
3221 /* Don't wait forever to paste */
3223 now
= GetTickCount();
3224 paste_diff
= now
- last_paste
;
3225 if (paste_diff
>= 0 && paste_diff
< 450)
3230 while (paste_pos
< paste_len
) {
3232 while (n
+ paste_pos
< paste_len
) {
3233 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3236 luni_send(paste_buffer
+ paste_pos
, n
);
3239 if (paste_pos
< paste_len
) {
3244 sfree(paste_buffer
);
3249 static void deselect(void)
3251 selstate
= NO_SELECTION
;
3252 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3255 void term_deselect(void)
3261 int term_ldisc(int option
)
3263 if (option
== LD_ECHO
)
3264 return term_echoing
;
3265 if (option
== LD_EDIT
)
3266 return term_editing
;
3271 * from_backend(), to get data from the backend for the terminal.
3273 void from_backend(int is_stderr
, char *data
, int len
)
3276 if (inbuf_head
>= INBUF_SIZE
)
3278 inbuf
[inbuf_head
++] = *data
++;
3283 * Log session traffic.
3285 void logtraffic(unsigned char c
, int logmode
)
3287 if (cfg
.logtype
> 0) {
3288 if (cfg
.logtype
== logmode
) {
3289 /* deferred open file from pgm start? */
3298 /* open log file append/overwrite mode */
3308 sprintf(writemod
, "wb"); /* default to rewrite */
3309 lgfp
= fopen(cfg
.logfilename
, "r"); /* file already present? */
3313 i
= askappend(cfg
.logfilename
);
3315 writemod
[0] = 'a'; /* set append mode */
3316 else if (i
== 0) { /* cancelled */
3318 cfg
.logtype
= 0; /* disable logging */
3323 lgfp
= fopen(cfg
.logfilename
, writemod
);
3324 if (lgfp
) { /* enter into event log */
3325 sprintf(buf
, "%s session log (%s mode) to file : ",
3326 (writemod
[0] == 'a') ?
"Appending" : "Writing new",
3327 (cfg
.logtype
== LGTYP_ASCII ?
"ASCII" :
3328 cfg
.logtype
== LGTYP_DEBUG ?
"raw" : "<ukwn>"));
3329 /* Make sure we do not exceed the output buffer size */
3330 strncat(buf
, cfg
.logfilename
, 128);
3331 buf
[strlen(buf
)] = '\0';
3334 /* --- write header line iinto log file */
3335 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp
);
3338 strftime(buf
, 24, "%Y.%m.%d %H:%M:%S", tm
);
3340 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp
);
3344 void logfclose(void)