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) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).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 vt52_bold
; /* Force bold on non-bold colours */
108 static int utf_state
; /* Is there a pending UTF-8 character */
109 static int utf_char
; /* and what is it so far. */
110 static int utf_size
; /* The size of the UTF character. */
112 static int xterm_mouse
; /* send mouse messages to app */
114 static unsigned long cset_attr
[2];
117 * Saved settings on the alternate screen.
119 static int alt_x
, alt_y
, alt_om
, alt_wrap
, alt_wnext
, alt_ins
, alt_cset
;
120 static int alt_t
, alt_b
;
121 static int alt_which
;
123 #define ARGS_MAX 32 /* max # of esc sequence arguments */
124 #define ARG_DEFAULT 0 /* if an arg isn't specified */
125 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
126 static int esc_args
[ARGS_MAX
];
127 static int esc_nargs
;
128 static int esc_query
;
129 #define ANSI(x,y) ((x)+((y)<<8))
130 #define ANSI_QUE(x) ANSI(x,TRUE)
132 #define OSC_STR_MAX 2048
133 static int osc_strlen
;
134 static char osc_string
[OSC_STR_MAX
+ 1];
137 static char id_string
[1024] = "\033[?6c";
139 static unsigned char *tabs
;
151 OSC_STRING
, OSC_MAYBE_ST
,
160 NO_SELECTION
, ABOUT_TO
, DRAGGING
, SELECTED
163 SM_CHAR
, SM_WORD
, SM_LINE
165 static pos selstart
, selend
, selanchor
;
167 static short wordness
[256] = {
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
170 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
171 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
172 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
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, 1, /* 67 */
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
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, /* AB */
180 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
181 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
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, /* EF */
186 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
187 static wchar_t sel_nl
[] = SEL_NL
;
188 static wchar_t *paste_buffer
= 0;
189 static int paste_len
, paste_pos
, paste_hold
;
192 * Internal prototypes.
194 static void do_paint(Context
, int);
195 static void erase_lots(int, int, int);
196 static void swap_screen(int);
197 static void update_sbar(void);
198 static void deselect(void);
199 /* log session to file stuff ... */
200 static FILE *lgfp
= NULL
;
201 static void logtraffic(unsigned char c
, int logmode
);
204 * Retrieve a line of the screen or of the scrollback, according to
205 * whether the y coordinate is non-negative or negative
208 unsigned long *lineptr(int y
, int lineno
)
210 unsigned long *line
, lineattrs
;
212 int i
, treeindex
, oldlen
;
218 whichtree
= scrollback
;
219 treeindex
= y
+ count234(scrollback
);
221 line
= index234(whichtree
, treeindex
);
223 /* We assume that we don't screw up and retrieve something out of range. */
224 assert(line
!= NULL
);
226 if (line
[0] != cols
) {
228 * This line is the wrong length, which probably means it
229 * hasn't been accessed since a resize. Resize it now.
232 lineattrs
= line
[oldlen
+ 1];
233 delpos234(whichtree
, treeindex
);
234 line
= srealloc(line
, TSIZE
* (2 + cols
));
236 for (i
= oldlen
; i
< cols
; i
++)
237 line
[i
+ 1] = ERASE_CHAR
;
238 line
[cols
+ 1] = lineattrs
& LATTR_MODE
;
239 addpos234(whichtree
, line
, treeindex
);
245 #define lineptr(x) lineptr(x,__LINE__)
247 * Set up power-on settings for the terminal.
249 static void power_on(void)
251 curs
.x
= curs
.y
= alt_x
= alt_y
= savecurs
.x
= savecurs
.y
= 0;
254 alt_b
= marg_b
= rows
- 1;
259 for (i
= 0; i
< cols
; i
++)
260 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
262 alt_om
= dec_om
= cfg
.dec_om
;
263 alt_wnext
= wrapnext
= alt_ins
= insert
= FALSE
;
264 alt_wrap
= wrap
= cfg
.wrap_mode
;
266 cset_attr
[0] = cset_attr
[1] = ATTR_ASCII
;
271 save_attr
= curr_attr
= ATTR_DEFAULT
;
272 term_editing
= term_echoing
= FALSE
;
273 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
274 app_cursor_keys
= cfg
.app_cursor
;
275 app_keypad_keys
= cfg
.app_keypad
;
277 blink_is_real
= cfg
.blinktext
;
278 erase_char
= ERASE_CHAR
;
282 for (i
= 0; i
< 256; i
++)
283 wordness
[i
] = cfg
.wordness
[i
];
287 erase_lots(FALSE
, TRUE
, TRUE
);
289 erase_lots(FALSE
, TRUE
, TRUE
);
294 * Force a screen update.
296 void term_update(void)
301 if ((seen_key_event
&& (cfg
.scroll_on_key
)) ||
302 (seen_disp_event
&& (cfg
.scroll_on_disp
))) {
303 disptop
= 0; /* return to main screen */
304 seen_disp_event
= seen_key_event
= 0;
308 sys_cursor(curs
.x
, curs
.y
- disptop
);
314 * Same as power_on(), but an external function.
316 void term_pwron(void)
326 * Clear the scrollback.
328 void term_clrsb(void)
332 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
339 * Initialise the terminal.
343 screen
= alt_screen
= scrollback
= NULL
;
345 disptext
= dispcurs
= NULL
;
350 beephead
= beeptail
= NULL
;
353 beep_overloaded
= FALSE
;
357 * Set up the terminal for a given size.
359 void term_size(int newrows
, int newcols
, int newsavelines
)
362 unsigned long *newdisp
, *line
;
365 int save_alt_which
= alt_which
;
367 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
368 return; /* nothing to do */
374 alt_b
= marg_b
= newrows
- 1;
377 scrollback
= newtree234(NULL
);
378 screen
= newtree234(NULL
);
383 * Resize the screen and scrollback. We only need to shift
384 * lines around within our data structures, because lineptr()
385 * will take care of resizing each individual line if
388 * - If the new screen and the old screen differ in length, we
389 * must shunt some lines in from the scrollback or out to
392 * - If doing that fails to provide us with enough material to
393 * fill the new screen (i.e. the number of rows needed in
394 * the new screen exceeds the total number in the previous
395 * screen+scrollback), we must invent some blank lines to
398 * - Then, if the new scrollback length is less than the
399 * amount of scrollback we actually have, we must throw some
402 sblen
= count234(scrollback
);
403 /* Do this loop to expand the screen if newrows > rows */
404 for (i
= rows
; i
< newrows
; i
++) {
406 line
= delpos234(scrollback
, --sblen
);
408 line
= smalloc(TSIZE
* (newcols
+ 2));
410 for (j
= 0; j
<= newcols
; j
++)
411 line
[j
+ 1] = ERASE_CHAR
;
413 addpos234(screen
, line
, 0);
415 /* Do this loop to shrink the screen if newrows < rows */
416 for (i
= newrows
; i
< rows
; i
++) {
417 line
= delpos234(screen
, 0);
418 addpos234(scrollback
, line
, sblen
++);
420 assert(count234(screen
) == newrows
);
421 while (sblen
> newsavelines
) {
422 line
= delpos234(scrollback
, 0);
426 assert(count234(scrollback
) <= newsavelines
);
429 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
430 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
431 newdisp
[i
] = ATTR_INVALID
;
436 newalt
= newtree234(NULL
);
437 for (i
= 0; i
< newrows
; i
++) {
438 line
= smalloc(TSIZE
* (newcols
+ 2));
440 for (j
= 0; j
<= newcols
; j
++)
441 line
[j
+ 1] = erase_char
;
442 addpos234(newalt
, line
, i
);
445 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
447 freetree234(alt_screen
);
451 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
454 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
455 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
459 curs
.y
+= newrows
- rows
;
462 if (curs
.y
>= newrows
)
463 curs
.y
= newrows
- 1;
464 if (curs
.x
>= newcols
)
465 curs
.x
= newcols
- 1;
467 wrapnext
= alt_wnext
= FALSE
;
471 savelines
= newsavelines
;
474 swap_screen(save_alt_which
);
483 static void swap_screen(int which
)
488 if (which
== alt_which
)
515 wrapnext
= alt_wnext
;
528 * Update the scroll bar.
530 static void update_sbar(void)
534 nscroll
= count234(scrollback
);
536 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
540 * Check whether the region bounded by the two pointers intersects
541 * the scroll region, and de-select the on-screen selection if so.
543 static void check_selection(pos from
, pos to
)
545 if (poslt(from
, selend
) && poslt(selstart
, to
))
550 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
551 * for backward.) `sb' is TRUE if the scrolling is permitted to
552 * affect the scrollback buffer.
554 * NB this function invalidates all pointers into lines of the
555 * screen data structures. In particular, you MUST call fix_cpos
556 * after calling scroll() and before doing anything else that
557 * uses the cpos shortcut pointer.
559 static void scroll(int topline
, int botline
, int lines
, int sb
)
561 unsigned long *line
, *line2
;
564 if (topline
!= 0 || alt_which
!= 0)
569 line
= delpos234(screen
, botline
);
570 for (i
= 0; i
< cols
; i
++)
571 line
[i
+ 1] = erase_char
;
573 addpos234(screen
, line
, topline
);
575 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
577 if (selstart
.y
> botline
) {
578 selstart
.y
= botline
;
582 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
584 if (selend
.y
> botline
) {
594 line
= delpos234(screen
, topline
);
595 if (sb
&& savelines
> 0) {
596 int sblen
= count234(scrollback
);
598 * We must add this line to the scrollback. We'll
599 * remove a line from the top of the scrollback to
600 * replace it, or allocate a new one if the
601 * scrollback isn't full.
603 if (sblen
== savelines
) {
604 sblen
--, line2
= delpos234(scrollback
, 0);
606 line2
= smalloc(TSIZE
* (cols
+ 2));
609 addpos234(scrollback
, line
, sblen
);
612 for (i
= 0; i
< cols
; i
++)
613 line
[i
+ 1] = erase_char
;
615 addpos234(screen
, line
, botline
);
617 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
619 if (selstart
.y
< topline
) {
620 selstart
.y
= topline
;
624 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
626 if (selend
.y
< topline
) {
638 * Move the cursor to a given position, clipping at boundaries. We
639 * may or may not want to clip at the scroll margin: marg_clip is 0
640 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
641 * even _being_ outside the margins.
643 static void move(int x
, int y
, int marg_clip
)
650 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
652 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
666 * Save or restore the cursor and SGR mode.
668 static void save_cursor(int save
)
672 save_attr
= curr_attr
;
674 save_csattr
= cset_attr
[cset
];
677 /* Make sure the window hasn't shrunk since the save */
683 curr_attr
= save_attr
;
685 cset_attr
[cset
] = save_csattr
;
688 erase_char
= (' ' | (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
693 * Erase a large portion of the screen: the whole screen, or the
694 * whole line, or parts thereof.
696 static void erase_lots(int line_only
, int from_begin
, int to_end
)
700 unsigned long *ldata
;
721 check_selection(start
, end
);
723 /* Clear screen also forces a full window redraw, just in case. */
724 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
727 ldata
= lineptr(start
.y
);
728 while (poslt(start
, end
)) {
729 if (start
.y
== cols
&& !erase_lattr
)
730 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
732 ldata
[start
.x
] = erase_char
;
733 if (incpos(start
) && start
.y
< rows
)
734 ldata
= lineptr(start
.y
);
739 * Insert or delete characters within the current line. n is +ve if
740 * insertion is desired, and -ve for deletion.
742 static void insch(int n
)
744 int dir
= (n
< 0 ?
-1 : +1);
747 unsigned long *ldata
;
749 n
= (n
< 0 ?
-n
: n
);
750 if (n
> cols
- curs
.x
)
752 m
= cols
- curs
.x
- n
;
754 cursplus
.x
= curs
.x
+ n
;
755 check_selection(curs
, cursplus
);
756 ldata
= lineptr(curs
.y
);
758 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
760 ldata
[curs
.x
+ m
++] = erase_char
;
762 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
764 ldata
[curs
.x
+ n
] = erase_char
;
769 * Toggle terminal mode `mode' to state `state'. (`query' indicates
770 * whether the mode is a DEC private one or a normal one.)
772 static void toggle_mode(int mode
, int query
, int state
)
778 case 1: /* application cursor keys */
779 app_cursor_keys
= state
;
781 case 2: /* VT52 mode */
784 blink_is_real
= FALSE
;
787 blink_is_real
= cfg
.blinktext
;
790 case 3: /* 80/132 columns */
792 request_resize(state ?
132 : 80, rows
, 1);
795 case 5: /* reverse video */
797 * Toggle reverse video. If we receive an OFF within the
798 * visual bell timeout period after an ON, we trigger an
799 * effective visual bell, so that ESC[?5hESC[?5l will
800 * always be an actually _visible_ visual bell.
802 ticks
= GetTickCount();
803 if (rvideo
&& !state
&& /* we're turning it off */
804 ticks
< rvbell_timeout
) { /* and it's not long since it was turned on */
805 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
806 if (vbell_timeout
< rvbell_timeout
) /* don't move vbell end forward */
807 vbell_timeout
= rvbell_timeout
; /* vbell end is at least then */
808 } else if (!rvideo
&& state
) {
809 /* This is an ON, so we notice the time and save it. */
810 rvbell_timeout
= ticks
+ VBELL_TIMEOUT
;
813 seen_disp_event
= TRUE
;
817 case 6: /* DEC origin mode */
820 case 7: /* auto wrap */
823 case 8: /* auto key repeat */
826 case 10: /* set local edit mode */
827 term_editing
= state
;
828 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
830 case 25: /* enable/disable cursor */
831 compatibility2(OTHER
, VT220
);
833 seen_disp_event
= TRUE
;
835 case 47: /* alternate screen */
836 compatibility(OTHER
);
841 case 1000: /* xterm mouse 1 */
842 xterm_mouse
= state ?
1 : 0;
843 set_raw_mouse_mode(state
);
845 case 1002: /* xterm mouse 2 */
846 xterm_mouse
= state ?
2 : 0;
847 set_raw_mouse_mode(state
);
851 case 4: /* set insert mode */
852 compatibility(VT102
);
855 case 12: /* set echo mode */
856 term_echoing
= !state
;
857 ldisc_send(NULL
, 0); /* cause ldisc to notice changes */
859 case 20: /* Return sends ... */
860 cr_lf_return
= state
;
862 case 34: /* Make cursor BIG */
863 compatibility2(OTHER
, VT220
);
869 * Process an OSC sequence: set window title or icon name.
871 static void do_osc(void)
875 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
877 osc_string
[osc_strlen
] = '\0';
878 switch (esc_args
[0]) {
881 set_icon(osc_string
);
882 if (esc_args
[0] == 1)
884 /* fall through: parameter 0 means set both */
887 set_title(osc_string
);
894 * Remove everything currently in `inbuf' and stick it up on the
895 * in-memory display. There's a big state machine in here to
896 * process escape sequences...
902 for (inbuf_reap
= 0; inbuf_reap
< inbuf_head
; inbuf_reap
++) {
903 c
= inbuf
[inbuf_reap
];
906 * Optionally log the session traffic to a file. Useful for
907 * debugging and possibly also useful for actual logging.
909 logtraffic((unsigned char) c
, LGTYP_DEBUG
);
911 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
912 * be able to display 8-bit characters, but I'll let that go 'cause
916 /* First see about all those translations. */
917 if (termstate
== TOPLEVEL
) {
922 /* I know; gotos are evil. This one is really bad!
923 * But before you try removing it follow the path of the
924 * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
927 if (cfg.no_vt_graph_with_utf8) break;
930 } else if ((c
& 0xe0) == 0xc0) {
931 utf_size
= utf_state
= 1;
932 utf_char
= (c
& 0x1f);
933 } else if ((c
& 0xf0) == 0xe0) {
934 utf_size
= utf_state
= 2;
935 utf_char
= (c
& 0x0f);
936 } else if ((c
& 0xf8) == 0xf0) {
937 utf_size
= utf_state
= 3;
938 utf_char
= (c
& 0x07);
939 } else if ((c
& 0xfc) == 0xf8) {
940 utf_size
= utf_state
= 4;
941 utf_char
= (c
& 0x03);
942 } else if ((c
& 0xfe) == 0xfc) {
943 utf_size
= utf_state
= 5;
944 utf_char
= (c
& 0x01);
955 if ((c
& 0xC0) != 0x80) {
956 inbuf_reap
--; /* This causes the faulting character */
957 c
= UCSERR
; /* to be logged twice - not really a */
958 utf_state
= 0; /* serious problem. */
961 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
967 /* Is somebody trying to be evil! */
969 (c
< 0x800 && utf_size
>= 2) ||
970 (c
< 0x10000 && utf_size
>= 3) ||
971 (c
< 0x200000 && utf_size
>= 4) ||
972 (c
< 0x4000000 && utf_size
>= 5))
975 /* Unicode line separator and paragraph separator are CR-LF */
976 if (c
== 0x2028 || c
== 0x2029)
979 /* High controls are probably a Baaad idea too. */
983 /* The UTF-16 surrogates are not nice either. */
984 /* The standard give the option of decoding these:
985 * I don't want to! */
986 if (c
>= 0xD800 && c
< 0xE000)
989 /* ISO 10646 characters now limited to UTF-16 range. */
993 /* This is currently a TagPhobic application.. */
994 if (c
>= 0xE0000 && c
<= 0xE007F)
997 /* U+FEFF is best seen as a null. */
1000 /* But U+FFFE is an error. */
1001 if (c
== 0xFFFE || c
== 0xFFFF)
1004 /* Oops this is a 16bit implementation */
1010 switch (cset_attr
[cset
]) {
1012 * Linedraw characters are different from 'ESC ( B'
1013 * only for a small range. For ones outside that
1014 * range, make sure we use the same font as well as
1015 * the same encoding.
1018 if (unitab_ctrl
[c
] != 0xFF)
1021 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1025 /* If UK-ASCII, make the '#' a LineDraw Pound */
1027 c
= '}' | ATTR_LINEDRW
;
1030 /*FALLTHROUGH*/ case ATTR_ASCII
:
1031 if (unitab_ctrl
[c
] != 0xFF)
1034 c
= ((unsigned char) c
) | ATTR_ASCII
;
1040 /* How about C1 controls ? */
1041 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1042 has_compat(VT220
)) {
1043 termstate
= SEEN_ESC
;
1045 c
= '@' + (c
& 0x1F);
1048 /* Or the GL control. */
1049 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1050 if (curs
.x
&& !wrapnext
)
1054 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1056 /* Or normal C0 controls. */
1057 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1059 case '\005': /* terminal type query */
1060 /* Strictly speaking this is VT100 but a VT100 defaults to
1061 * no response. Other terminals respond at their option.
1063 * Don't put a CR in the default string as this tends to
1064 * upset some weird software.
1066 * An xterm returns "xterm" (5 characters)
1068 compatibility(ANSIMIN
);
1070 char abuf
[256], *s
, *d
;
1072 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1074 if (*s
>= 'a' && *s
<= 'z')
1075 *d
++ = (*s
- ('a' - 1));
1076 else if ((*s
>= '@' && *s
<= '_') ||
1077 *s
== '?' || (*s
& 0x80))
1082 } else if (*s
== '^') {
1087 lpage_send(CP_ACP
, abuf
, d
- abuf
);
1092 struct beeptime
*newbeep
;
1095 ticks
= GetTickCount();
1097 if (!beep_overloaded
) {
1098 newbeep
= smalloc(sizeof(struct beeptime
));
1099 newbeep
->ticks
= ticks
;
1100 newbeep
->next
= NULL
;
1104 beeptail
->next
= newbeep
;
1110 * Throw out any beeps that happened more than
1114 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1115 struct beeptime
*tmp
= beephead
;
1116 beephead
= tmp
->next
;
1123 if (cfg
.bellovl
&& beep_overloaded
&&
1124 ticks
- lastbeep
>= cfg
.bellovl_s
) {
1126 * If we're currently overloaded and the
1127 * last beep was more than s seconds ago,
1128 * leave overload mode.
1130 beep_overloaded
= FALSE
;
1131 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1132 nbeeps
>= cfg
.bellovl_n
) {
1134 * Now, if we have n or more beeps
1135 * remaining in the queue, go into overload
1138 beep_overloaded
= TRUE
;
1143 * Perform an actual beep if we're not overloaded.
1145 if ((!cfg
.bellovl
|| !beep_overloaded
)
1149 else if (cfg
.beep
== 2) {
1151 vbell_timeout
= ticks
+ VBELL_TIMEOUT
;
1159 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1160 else if (curs
.x
== 0 && curs
.y
> 0)
1161 curs
.x
= cols
- 1, curs
.y
--;
1167 seen_disp_event
= TRUE
;
1170 compatibility(VT100
);
1174 compatibility(VT100
);
1179 termstate
= VT52_ESC
;
1181 compatibility(ANSIMIN
);
1182 termstate
= SEEN_ESC
;
1190 seen_disp_event
= TRUE
;
1192 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1195 if (has_compat(SCOANSI
)) {
1197 erase_lots(FALSE
, FALSE
, TRUE
);
1200 seen_disp_event
= 1;
1204 compatibility(VT100
);
1206 if (curs
.y
== marg_b
)
1207 scroll(marg_t
, marg_b
, 1, TRUE
);
1208 else if (curs
.y
< rows
- 1)
1214 seen_disp_event
= 1;
1216 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1220 pos old_curs
= curs
;
1221 unsigned long *ldata
= lineptr(curs
.y
);
1225 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1227 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1228 if (curs
.x
>= cols
/ 2)
1229 curs
.x
= cols
/ 2 - 1;
1236 check_selection(old_curs
, curs
);
1238 seen_disp_event
= TRUE
;
1242 switch (termstate
) {
1244 /* Only graphic characters get this far, ctrls are stripped above */
1245 if (wrapnext
&& wrap
) {
1246 cpos
[1] |= LATTR_WRAPPED
;
1247 if (curs
.y
== marg_b
)
1248 scroll(marg_t
, marg_b
, 1, TRUE
);
1249 else if (curs
.y
< rows
- 1)
1257 if (selstate
!= NO_SELECTION
) {
1258 pos cursplus
= curs
;
1260 check_selection(curs
, cursplus
);
1262 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1263 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1265 extern int wcwidth(wchar_t ucs
);
1270 width
= wcwidth((wchar_t) c
);
1273 if (curs
.x
+ 1 != cols
) {
1274 *cpos
++ = c
| ATTR_WIDE
| curr_attr
;
1275 *cpos
++ = UCSWIDE
| curr_attr
;
1280 *cpos
++ = c
| curr_attr
;
1287 if (curs
.x
== cols
) {
1291 if (wrap
&& vt52_mode
) {
1292 cpos
[1] |= LATTR_WRAPPED
;
1293 if (curs
.y
== marg_b
)
1294 scroll(marg_t
, marg_b
, 1, TRUE
);
1295 else if (curs
.y
< rows
- 1)
1302 seen_disp_event
= 1;
1307 * This state is virtually identical to SEEN_ESC, with the
1308 * exception that we have an OSC sequence in the pipeline,
1309 * and _if_ we see a backslash, we process it.
1313 termstate
= TOPLEVEL
;
1316 /* else fall through */
1318 if (c
>= ' ' && c
<= '/') {
1325 termstate
= TOPLEVEL
;
1326 switch (ANSI(c
, esc_query
)) {
1327 case '[': /* enter CSI mode */
1328 termstate
= SEEN_CSI
;
1330 esc_args
[0] = ARG_DEFAULT
;
1333 case ']': /* xterm escape sequences */
1334 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1335 compatibility(OTHER
);
1336 termstate
= SEEN_OSC
;
1339 case '7': /* save cursor */
1340 compatibility(VT100
);
1343 case '8': /* restore cursor */
1344 compatibility(VT100
);
1346 seen_disp_event
= TRUE
;
1349 compatibility(VT100
);
1350 app_keypad_keys
= TRUE
;
1353 compatibility(VT100
);
1354 app_keypad_keys
= FALSE
;
1356 case 'D': /* exactly equivalent to LF */
1357 compatibility(VT100
);
1358 if (curs
.y
== marg_b
)
1359 scroll(marg_t
, marg_b
, 1, TRUE
);
1360 else if (curs
.y
< rows
- 1)
1364 seen_disp_event
= TRUE
;
1366 case 'E': /* exactly equivalent to CR-LF */
1367 compatibility(VT100
);
1369 if (curs
.y
== marg_b
)
1370 scroll(marg_t
, marg_b
, 1, TRUE
);
1371 else if (curs
.y
< rows
- 1)
1375 seen_disp_event
= TRUE
;
1377 case 'M': /* reverse index - backwards LF */
1378 compatibility(VT100
);
1379 if (curs
.y
== marg_t
)
1380 scroll(marg_t
, marg_b
, -1, TRUE
);
1381 else if (curs
.y
> 0)
1385 seen_disp_event
= TRUE
;
1387 case 'Z': /* terminal type query */
1388 compatibility(VT100
);
1389 ldisc_send(id_string
, strlen(id_string
));
1391 case 'c': /* restore power-on settings */
1392 compatibility(VT100
);
1395 request_resize(80, rows
, 1);
1400 seen_disp_event
= TRUE
;
1402 case 'H': /* set a tab */
1403 compatibility(VT100
);
1404 tabs
[curs
.x
] = TRUE
;
1407 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1408 compatibility(VT100
);
1410 unsigned long *ldata
;
1414 for (i
= 0; i
< rows
; i
++) {
1416 for (j
= 0; j
< cols
; j
++)
1417 ldata
[j
] = ATTR_DEFAULT
| 'E';
1421 seen_disp_event
= TRUE
;
1422 scrtop
.x
= scrtop
.y
= 0;
1425 check_selection(scrtop
, scrbot
);
1429 case ANSI('3', '#'):
1430 case ANSI('4', '#'):
1431 case ANSI('5', '#'):
1432 case ANSI('6', '#'):
1433 compatibility(VT100
);
1435 unsigned long nlattr
;
1436 unsigned long *ldata
;
1437 switch (ANSI(c
, esc_query
)) {
1438 case ANSI('3', '#'):
1441 case ANSI('4', '#'):
1444 case ANSI('5', '#'):
1445 nlattr
= LATTR_NORM
;
1447 default: /* spiritually case ANSI('6', '#'): */
1448 nlattr
= LATTR_WIDE
;
1451 ldata
= lineptr(curs
.y
);
1452 ldata
[cols
] &= ~LATTR_MODE
;
1453 ldata
[cols
] |= nlattr
;
1457 case ANSI('A', '('):
1458 compatibility(VT100
);
1459 cset_attr
[0] = ATTR_GBCHR
;
1461 case ANSI('B', '('):
1462 compatibility(VT100
);
1463 cset_attr
[0] = ATTR_ASCII
;
1465 case ANSI('0', '('):
1466 compatibility(VT100
);
1467 cset_attr
[0] = ATTR_LINEDRW
;
1470 case ANSI('A', ')'):
1471 compatibility(VT100
);
1472 cset_attr
[1] = ATTR_GBCHR
;
1474 case ANSI('B', ')'):
1475 compatibility(VT100
);
1476 cset_attr
[1] = ATTR_ASCII
;
1478 case ANSI('0', ')'):
1479 compatibility(VT100
);
1480 cset_attr
[1] = ATTR_LINEDRW
;
1483 case ANSI('8', '%'): /* Old Linux code */
1484 case ANSI('G', '%'):
1485 compatibility(OTHER
);
1488 case ANSI('@', '%'):
1489 compatibility(OTHER
);
1490 if (line_codepage
!= CP_UTF8
)
1496 termstate
= TOPLEVEL
; /* default */
1498 if (esc_nargs
<= ARGS_MAX
) {
1499 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1500 esc_args
[esc_nargs
- 1] = 0;
1501 esc_args
[esc_nargs
- 1] =
1502 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1504 termstate
= SEEN_CSI
;
1505 } else if (c
== ';') {
1506 if (++esc_nargs
<= ARGS_MAX
)
1507 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1508 termstate
= SEEN_CSI
;
1509 } else if (c
< '@') {
1516 termstate
= SEEN_CSI
;
1518 switch (ANSI(c
, esc_query
)) {
1519 case 'A': /* move up N lines */
1520 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1521 seen_disp_event
= TRUE
;
1523 case 'e': /* move down N lines */
1524 compatibility(ANSI
);
1526 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1527 seen_disp_event
= TRUE
;
1529 case 'a': /* move right N cols */
1530 compatibility(ANSI
);
1531 case ANSI('c', '>'): /* report xterm version */
1532 compatibility(OTHER
);
1533 /* this reports xterm version 136 so that VIM can
1534 use the drag messages from the mouse reporting */
1535 ldisc_send("\033[>0;136;0c", 11);
1538 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1539 seen_disp_event
= TRUE
;
1541 case 'D': /* move left N cols */
1542 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1543 seen_disp_event
= TRUE
;
1545 case 'E': /* move down N lines and CR */
1546 compatibility(ANSI
);
1547 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1548 seen_disp_event
= TRUE
;
1550 case 'F': /* move up N lines and CR */
1551 compatibility(ANSI
);
1552 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1553 seen_disp_event
= TRUE
;
1556 case '`': /* set horizontal posn */
1557 compatibility(ANSI
);
1558 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1559 seen_disp_event
= TRUE
;
1561 case 'd': /* set vertical posn */
1562 compatibility(ANSI
);
1564 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1567 seen_disp_event
= TRUE
;
1570 case 'f': /* set horz and vert posns at once */
1572 esc_args
[1] = ARG_DEFAULT
;
1573 move(def(esc_args
[1], 1) - 1,
1574 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1577 seen_disp_event
= TRUE
;
1579 case 'J': /* erase screen or parts of it */
1581 unsigned int i
= def(esc_args
[0], 0) + 1;
1584 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1587 seen_disp_event
= TRUE
;
1589 case 'K': /* erase line or parts of it */
1591 unsigned int i
= def(esc_args
[0], 0) + 1;
1594 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1596 seen_disp_event
= TRUE
;
1598 case 'L': /* insert lines */
1599 compatibility(VT102
);
1600 if (curs
.y
<= marg_b
)
1601 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1604 seen_disp_event
= TRUE
;
1606 case 'M': /* delete lines */
1607 compatibility(VT102
);
1608 if (curs
.y
<= marg_b
)
1609 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1612 seen_disp_event
= TRUE
;
1614 case '@': /* insert chars */
1615 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1616 compatibility(VT102
);
1617 insch(def(esc_args
[0], 1));
1618 seen_disp_event
= TRUE
;
1620 case 'P': /* delete chars */
1621 compatibility(VT102
);
1622 insch(-def(esc_args
[0], 1));
1623 seen_disp_event
= TRUE
;
1625 case 'c': /* terminal type query */
1626 compatibility(VT100
);
1627 /* This is the response for a VT102 */
1628 ldisc_send(id_string
, strlen(id_string
));
1630 case 'n': /* cursor position query */
1631 if (esc_args
[0] == 6) {
1633 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1635 ldisc_send(buf
, strlen(buf
));
1636 } else if (esc_args
[0] == 5) {
1637 ldisc_send("\033[0n", 4);
1640 case 'h': /* toggle modes to high */
1642 compatibility(VT100
);
1645 for (i
= 0; i
< esc_nargs
; i
++)
1646 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1649 case 'l': /* toggle modes to low */
1651 compatibility(VT100
);
1654 for (i
= 0; i
< esc_nargs
; i
++)
1655 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1658 case 'g': /* clear tabs */
1659 compatibility(VT100
);
1660 if (esc_nargs
== 1) {
1661 if (esc_args
[0] == 0) {
1662 tabs
[curs
.x
] = FALSE
;
1663 } else if (esc_args
[0] == 3) {
1665 for (i
= 0; i
< cols
; i
++)
1670 case 'r': /* set scroll margins */
1671 compatibility(VT100
);
1672 if (esc_nargs
<= 2) {
1674 top
= def(esc_args
[0], 1) - 1;
1675 bot
= (esc_nargs
<= 1
1677 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1680 /* VTTEST Bug 9 - if region is less than 2 lines
1681 * don't change region.
1683 if (bot
- top
> 0) {
1688 * I used to think the cursor should be
1689 * placed at the top of the newly marginned
1690 * area. Apparently not: VMS TPU falls over
1693 * Well actually it should for Origin mode - RDB
1695 curs
.y
= (dec_om ? marg_t
: 0);
1697 seen_disp_event
= TRUE
;
1701 case 'm': /* set graphics rendition */
1704 * A VT100 without the AVO only had one attribute, either
1705 * underline or reverse video depending on the cursor type,
1706 * this was selected by CSI 7m.
1709 * This is sometimes DIM, eg on the GIGI and Linux
1711 * This is sometimes INVIS various ANSI.
1713 * This like 22 disables BOLD, DIM and INVIS
1715 * The ANSI colours appear on any terminal that has colour
1716 * (obviously) but the interaction between sgr0 and the
1717 * colours varies but is usually related to the background
1718 * colour erase item.
1719 * The interaction between colour attributes and the mono
1720 * ones is also very implementation dependent.
1722 * The 39 and 49 attributes are likely to be unimplemented.
1725 for (i
= 0; i
< esc_nargs
; i
++) {
1726 switch (def(esc_args
[i
], 0)) {
1727 case 0: /* restore defaults */
1728 curr_attr
= ATTR_DEFAULT
;
1730 case 1: /* enable bold */
1731 compatibility(VT100AVO
);
1732 curr_attr
|= ATTR_BOLD
;
1734 case 21: /* (enable double underline) */
1735 compatibility(OTHER
);
1736 case 4: /* enable underline */
1737 compatibility(VT100AVO
);
1738 curr_attr
|= ATTR_UNDER
;
1740 case 5: /* enable blink */
1741 compatibility(VT100AVO
);
1742 curr_attr
|= ATTR_BLINK
;
1744 case 7: /* enable reverse video */
1745 curr_attr
|= ATTR_REVERSE
;
1747 case 22: /* disable bold */
1748 compatibility2(OTHER
, VT220
);
1749 curr_attr
&= ~ATTR_BOLD
;
1751 case 24: /* disable underline */
1752 compatibility2(OTHER
, VT220
);
1753 curr_attr
&= ~ATTR_UNDER
;
1755 case 25: /* disable blink */
1756 compatibility2(OTHER
, VT220
);
1757 curr_attr
&= ~ATTR_BLINK
;
1759 case 27: /* disable reverse video */
1760 compatibility2(OTHER
, VT220
);
1761 curr_attr
&= ~ATTR_REVERSE
;
1772 curr_attr
&= ~ATTR_FGMASK
;
1774 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1776 case 39: /* default-foreground */
1777 curr_attr
&= ~ATTR_FGMASK
;
1778 curr_attr
|= ATTR_DEFFG
;
1789 curr_attr
&= ~ATTR_BGMASK
;
1791 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1793 case 49: /* default-background */
1794 curr_attr
&= ~ATTR_BGMASK
;
1795 curr_attr
|= ATTR_DEFBG
;
1803 (ATTR_FGMASK
| ATTR_BGMASK
|
1807 case 's': /* save cursor */
1810 case 'u': /* restore cursor */
1812 seen_disp_event
= TRUE
;
1814 case 't': /* set page size - ie window height */
1816 * VT340/VT420 sequence DECSLPP, DEC only allows values
1817 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1818 * illegal values (eg first arg 1..9) for window changing
1821 compatibility(VT340TEXT
);
1823 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1824 request_resize(cols
, def(esc_args
[0], 24), 0);
1829 compatibility(SCOANSI
);
1830 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
1833 seen_disp_event
= TRUE
;
1836 compatibility(SCOANSI
);
1837 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
1840 seen_disp_event
= TRUE
;
1842 case ANSI('|', '*'):
1843 /* VT420 sequence DECSNLS
1844 * Set number of lines on screen
1845 * VT420 uses VGA like hardware and can support any size in
1846 * reasonable range (24..49 AIUI) with no default specified.
1848 compatibility(VT420
);
1849 if (esc_nargs
== 1 && esc_args
[0] > 0) {
1850 request_resize(cols
,
1851 def(esc_args
[0], cfg
.height
),
1856 case ANSI('|', '$'):
1857 /* VT340/VT420 sequence DECSCPP
1858 * Set number of columns per page
1859 * Docs imply range is only 80 or 132, but I'll allow any.
1861 compatibility(VT340TEXT
);
1862 if (esc_nargs
<= 1) {
1863 request_resize(def(esc_args
[0], cfg
.width
),
1868 case 'X': /* write N spaces w/o moving cursor */
1869 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1870 compatibility(ANSIMIN
);
1872 int n
= def(esc_args
[0], 1);
1874 unsigned long *p
= cpos
;
1875 if (n
> cols
- curs
.x
)
1879 check_selection(curs
, cursplus
);
1882 seen_disp_event
= TRUE
;
1885 case 'x': /* report terminal characteristics */
1886 compatibility(VT100
);
1889 int i
= def(esc_args
[0], 0);
1890 if (i
== 0 || i
== 1) {
1891 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
1893 ldisc_send(buf
, 20);
1897 case ANSI('L', '='):
1898 compatibility(OTHER
);
1899 use_bce
= (esc_args
[0] <= 0);
1900 erase_char
= ERASE_CHAR
;
1905 (ATTR_FGMASK
| ATTR_BGMASK
)));
1907 case ANSI('E', '='):
1908 compatibility(OTHER
);
1909 blink_is_real
= (esc_args
[0] >= 1);
1911 case ANSI('p', '"'):
1912 /* Allow the host to make this emulator a 'perfect' VT102.
1913 * This first appeared in the VT220, but we do need to get
1914 * back to PuTTY mode so I won't check it.
1916 * The arg in 40..42,50 are a PuTTY extension.
1917 * The 2nd arg, 8bit vs 7bit is not checked.
1919 * Setting VT102 mode should also change the Fkeys to
1920 * generate PF* codes as a real VT102 has no Fkeys.
1921 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1924 * Note ESC c will NOT change this!
1927 switch (esc_args
[0]) {
1929 compatibility_level
&= ~TM_VTXXX
;
1930 compatibility_level
|= TM_VT102
;
1933 compatibility_level
&= ~TM_VTXXX
;
1934 compatibility_level
|= TM_VT220
;
1938 if (esc_args
[0] > 60 && esc_args
[0] < 70)
1939 compatibility_level
|= TM_VTXXX
;
1943 compatibility_level
&= TM_VTXXX
;
1946 compatibility_level
= TM_PUTTY
;
1949 compatibility_level
= TM_SCOANSI
;
1953 compatibility_level
= TM_PUTTY
;
1959 /* Change the response to CSI c */
1960 if (esc_args
[0] == 50) {
1963 strcpy(id_string
, "\033[?");
1964 for (i
= 1; i
< esc_nargs
; i
++) {
1966 strcat(id_string
, ";");
1967 sprintf(lbuf
, "%d", esc_args
[i
]);
1968 strcat(id_string
, lbuf
);
1970 strcat(id_string
, "c");
1973 /* Is this a good idea ?
1974 * Well we should do a soft reset at this point ...
1976 if (!has_compat(VT420
) && has_compat(VT100
)) {
1978 request_resize(132, 24, 1);
1980 request_resize(80, 24, 1);
1989 case 'P': /* Linux palette sequence */
1990 termstate
= SEEN_OSC_P
;
1993 case 'R': /* Linux palette reset */
1996 termstate
= TOPLEVEL
;
1998 case 'W': /* word-set */
1999 termstate
= SEEN_OSC_W
;
2012 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2016 * Grotty hack to support xterm and DECterm title
2017 * sequences concurrently.
2019 if (esc_args
[0] == 2) {
2023 /* else fall through */
2025 termstate
= OSC_STRING
;
2031 * This OSC stuff is EVIL. It takes just one character to get into
2032 * sysline mode and it's not initially obvious how to get out.
2033 * So I've added CR and LF as string aborts.
2034 * This shouldn't effect compatibility as I believe embedded
2035 * control characters are supposed to be interpreted (maybe?)
2036 * and they don't display anything useful anyway.
2040 if (c
== '\n' || c
== '\r') {
2041 termstate
= TOPLEVEL
;
2042 } else if (c
== 0234 || c
== '\007') {
2044 * These characters terminate the string; ST and BEL
2045 * terminate the sequence and trigger instant
2046 * processing of it, whereas ESC goes back to SEEN_ESC
2047 * mode unless it is followed by \, in which case it is
2048 * synonymous with ST in the first place.
2051 termstate
= TOPLEVEL
;
2052 } else if (c
== '\033')
2053 termstate
= OSC_MAYBE_ST
;
2054 else if (osc_strlen
< OSC_STR_MAX
)
2055 osc_string
[osc_strlen
++] = c
;
2059 int max
= (osc_strlen
== 0 ?
21 : 16);
2061 if (c
>= '0' && c
<= '9')
2063 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2065 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2068 termstate
= TOPLEVEL
;
2071 osc_string
[osc_strlen
++] = val
;
2072 if (osc_strlen
>= 7) {
2073 palette_set(osc_string
[0],
2074 osc_string
[1] * 16 + osc_string
[2],
2075 osc_string
[3] * 16 + osc_string
[4],
2076 osc_string
[5] * 16 + osc_string
[6]);
2078 termstate
= TOPLEVEL
;
2094 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2097 termstate
= OSC_STRING
;
2102 termstate
= TOPLEVEL
;
2103 seen_disp_event
= TRUE
;
2106 move(curs
.x
, curs
.y
- 1, 1);
2109 move(curs
.x
, curs
.y
+ 1, 1);
2112 move(curs
.x
+ 1, curs
.y
, 1);
2115 move(curs
.x
- 1, curs
.y
, 1);
2118 * From the VT100 Manual
2119 * NOTE: The special graphics characters in the VT100
2120 * are different from those in the VT52
2122 * From VT102 manual:
2123 * 137 _ Blank - Same
2124 * 140 ` Reserved - Humm.
2125 * 141 a Solid rectangle - Similar
2126 * 142 b 1/ - Top half of fraction for the
2127 * 143 c 3/ - subscript numbers below.
2130 * 146 f Degrees - Same
2131 * 147 g Plus or minus - Same
2133 * 151 i Ellipsis (dots)
2136 * 154 l Bar at scan 0
2137 * 155 m Bar at scan 1
2138 * 156 n Bar at scan 2
2139 * 157 o Bar at scan 3 - Similar
2140 * 160 p Bar at scan 4 - Similar
2141 * 161 q Bar at scan 5 - Similar
2142 * 162 r Bar at scan 6 - Same
2143 * 163 s Bar at scan 7 - Similar
2158 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2161 cset_attr
[cset
= 0] = ATTR_ASCII
;
2168 scroll(0, rows
- 1, -1, TRUE
);
2169 else if (curs
.y
> 0)
2175 erase_lots(FALSE
, FALSE
, TRUE
);
2179 erase_lots(TRUE
, FALSE
, TRUE
);
2183 /* XXX Print cursor line */
2186 /* XXX Start controller mode */
2189 /* XXX Stop controller mode */
2193 termstate
= VT52_Y1
;
2196 ldisc_send("\033/Z", 3);
2199 app_keypad_keys
= TRUE
;
2202 app_keypad_keys
= FALSE
;
2205 /* XXX This should switch to VT100 mode not current or default
2206 * VT mode. But this will only have effect in a VT220+
2210 blink_is_real
= cfg
.blinktext
;
2214 /* XXX Enter auto print mode */
2217 /* XXX Exit auto print mode */
2220 /* XXX Print screen */
2226 /* compatibility(ATARI) */
2228 erase_lots(FALSE
, FALSE
, TRUE
);
2232 /* compatibility(ATARI) */
2233 if (curs
.y
<= marg_b
)
2234 scroll(curs
.y
, marg_b
, -1, FALSE
);
2237 /* compatibility(ATARI) */
2238 if (curs
.y
<= marg_b
)
2239 scroll(curs
.y
, marg_b
, 1, TRUE
);
2242 /* compatibility(ATARI) */
2243 termstate
= VT52_FG
;
2246 /* compatibility(ATARI) */
2247 termstate
= VT52_BG
;
2250 /* compatibility(ATARI) */
2251 erase_lots(FALSE
, TRUE
, FALSE
);
2255 /* compatibility(ATARI) */
2259 /* compatibility(ATARI) */
2262 /* case 'j': Save cursor position - broken on ST */
2263 /* case 'k': Restore cursor position */
2265 /* compatibility(ATARI) */
2266 erase_lots(TRUE
, TRUE
, TRUE
);
2272 /* compatibility(ATARI) */
2273 erase_lots(TRUE
, TRUE
, FALSE
);
2276 /* compatibility(ATARI) */
2277 curr_attr
|= ATTR_REVERSE
;
2280 /* compatibility(ATARI) */
2281 curr_attr
&= ~ATTR_REVERSE
;
2283 case 'v': /* wrap Autowrap on - Wyse style */
2284 /* compatibility(ATARI) */
2287 case 'w': /* Autowrap off */
2288 /* compatibility(ATARI) */
2293 /* compatibility(OTHER) */
2295 curr_attr
= ATTR_DEFAULT
;
2299 (ATTR_FGMASK
| ATTR_BGMASK
|
2303 /* compatibility(VI50) */
2304 curr_attr
|= ATTR_UNDER
;
2307 /* compatibility(VI50) */
2308 curr_attr
&= ~ATTR_UNDER
;
2311 /* compatibility(VI50) */
2313 curr_attr
|= ATTR_BOLD
;
2316 /* compatibility(VI50) */
2318 curr_attr
&= ~ATTR_BOLD
;
2324 termstate
= VT52_Y2
;
2325 move(curs
.x
, c
- ' ', 0);
2328 termstate
= TOPLEVEL
;
2329 move(c
- ' ', curs
.y
, 0);
2334 termstate
= TOPLEVEL
;
2335 curr_attr
&= ~ATTR_FGMASK
;
2336 curr_attr
&= ~ATTR_BOLD
;
2337 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2338 if ((c
& 0x8) || vt52_bold
)
2339 curr_attr
|= ATTR_BOLD
;
2344 (ATTR_FGMASK
| ATTR_BGMASK
|
2348 termstate
= TOPLEVEL
;
2349 curr_attr
&= ~ATTR_BGMASK
;
2350 curr_attr
&= ~ATTR_BLINK
;
2351 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2353 /* Note: bold background */
2355 curr_attr
|= ATTR_BLINK
;
2360 (ATTR_FGMASK
| ATTR_BGMASK
|
2364 default: break; /* placate gcc warning about enum use */
2366 if (selstate
!= NO_SELECTION
) {
2367 pos cursplus
= curs
;
2369 check_selection(curs
, cursplus
);
2377 * Compare two lines to determine whether they are sufficiently
2378 * alike to scroll-optimise one to the other. Return the degree of
2381 static int linecmp(unsigned long *a
, unsigned long *b
)
2385 for (i
= n
= 0; i
< cols
; i
++)
2386 n
+= (*a
++ == *b
++);
2392 * Given a context, update the window. Out of paranoia, we don't
2393 * allow WM_PAINT responses to do scrolling optimisations.
2395 static void do_paint(Context ctx
, int may_optimise
)
2397 int i
, j
, our_curs_y
;
2398 unsigned long rv
, cursor
;
2401 long cursor_background
= ERASE_CHAR
;
2405 * Check the visual bell state.
2408 ticks
= GetTickCount();
2409 if (ticks
- vbell_timeout
>= 0)
2413 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2416 * screen array, disptop, scrtop,
2418 * cfg.blinkpc, blink_is_real, tblinker,
2419 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2422 /* Has the cursor position or type changed ? */
2425 if (blinker
|| !cfg
.blink_cur
)
2426 cursor
= TATTR_ACTCURS
;
2430 cursor
= TATTR_PASCURS
;
2432 cursor
|= TATTR_RIGHTCURS
;
2435 our_curs_y
= curs
.y
- disptop
;
2437 if (dispcurs
&& (curstype
!= cursor
||
2439 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2440 if (dispcurs
> disptext
&& (dispcurs
[-1] & ATTR_WIDE
))
2441 dispcurs
[-1] |= ATTR_INVALID
;
2442 if ((*dispcurs
& ATTR_WIDE
))
2443 dispcurs
[1] |= ATTR_INVALID
;
2444 *dispcurs
|= ATTR_INVALID
;
2449 /* The normal screen data */
2450 for (i
= 0; i
< rows
; i
++) {
2451 unsigned long *ldata
;
2453 int idx
, dirty_line
, dirty_run
;
2454 unsigned long attr
= 0;
2455 int updated_line
= 0;
2458 int last_run_dirty
= 0;
2460 scrpos
.y
= i
+ disptop
;
2461 ldata
= lineptr(scrpos
.y
);
2462 lattr
= (ldata
[cols
] & LATTR_MODE
);
2464 idx
= i
* (cols
+ 1);
2465 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2466 disptext
[idx
+ cols
] = ldata
[cols
];
2468 for (j
= 0; j
< cols
; j
++, idx
++) {
2469 unsigned long tattr
, tchar
;
2470 unsigned long *d
= ldata
+ j
;
2474 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2475 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2476 switch (tchar
& CSET_MASK
) {
2478 tchar
= unitab_line
[tchar
& 0xFF];
2481 tchar
= unitab_xterm
[tchar
& 0xFF];
2484 tattr
|= (tchar
& CSET_MASK
);
2487 /* Video reversing things */
2489 ^ (posle(selstart
, scrpos
) &&
2490 poslt(scrpos
, selend
) ? ATTR_REVERSE
: 0));
2492 /* 'Real' blinking ? */
2493 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2494 if (has_focus
&& tblinker
) {
2496 tattr
&= ~CSET_MASK
;
2499 tattr
&= ~ATTR_BLINK
;
2502 /* Cursor here ? Save the 'background' */
2503 if (i
== our_curs_y
&& j
== curs
.x
) {
2504 cursor_background
= tattr
| tchar
;
2505 dispcurs
= disptext
+ idx
;
2508 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2511 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2513 /* Special hack for VT100 Linedraw glyphs */
2514 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2515 && tchar
<= 0xBD) break_run
= TRUE
;
2517 if (!dbcs_screenfont
&& !dirty_line
) {
2518 if ((tchar
| tattr
) == disptext
[idx
])
2520 else if (!dirty_run
&& ccount
== 1)
2525 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2526 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2532 if (dbcs_screenfont
)
2533 last_run_dirty
= dirty_run
;
2534 dirty_run
= dirty_line
;
2537 if ((tchar
| tattr
) != disptext
[idx
])
2539 ch
[ccount
++] = (char) tchar
;
2540 disptext
[idx
] = tchar
| tattr
;
2542 /* If it's a wide char step along to the next one. */
2543 if (tattr
& ATTR_WIDE
) {
2547 /* Cursor is here ? Ouch! */
2548 if (i
== our_curs_y
&& j
== curs
.x
) {
2549 cursor_background
= *d
;
2550 dispcurs
= disptext
+ idx
;
2552 if (disptext
[idx
] != *d
)
2558 if (dirty_run
&& ccount
> 0) {
2559 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2563 /* Cursor on this line ? (and changed) */
2564 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2565 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2566 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2567 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2574 * Flick the switch that says if blinking things should be shown or hidden.
2577 void term_blink(int flg
)
2579 static long last_blink
= 0;
2580 static long last_tblink
= 0;
2581 long now
, blink_diff
;
2583 now
= GetTickCount();
2584 blink_diff
= now
- last_tblink
;
2586 /* Make sure the text blinks no more than 2Hz */
2587 if (blink_diff
< 0 || blink_diff
> 450) {
2589 tblinker
= !tblinker
;
2598 blink_diff
= now
- last_blink
;
2600 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2601 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2609 * Invalidate the whole screen so it will be repainted in full.
2611 void term_invalidate(void)
2615 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2616 disptext
[i
] = ATTR_INVALID
;
2620 * Paint the window in response to a WM_PAINT message.
2622 void term_paint(Context ctx
, int l
, int t
, int r
, int b
)
2624 int i
, j
, left
, top
, right
, bottom
;
2626 left
= l
/ font_width
;
2627 right
= (r
- 1) / font_width
;
2628 top
= t
/ font_height
;
2629 bottom
= (b
- 1) / font_height
;
2630 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2631 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2632 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2633 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2635 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2636 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2639 /* This should happen soon enough, also for some reason it sometimes
2640 * fails to actually do anything when re-sizing ... painting the wrong
2642 do_paint (ctx, FALSE);
2647 * Attempt to scroll the scrollback. The second parameter gives the
2648 * position we want to scroll to; the first is +1 to denote that
2649 * this position is relative to the beginning of the scrollback, -1
2650 * to denote it is relative to the end, and 0 to denote that it is
2651 * relative to the current position.
2653 void term_scroll(int rel
, int where
)
2655 int sbtop
= -count234(scrollback
);
2657 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2658 if (disptop
< sbtop
)
2666 static void clipme(pos top
, pos bottom
)
2669 wchar_t *wbptr
; /* where next char goes within workbuf */
2670 int wblen
= 0; /* workbuf len */
2671 int buflen
; /* amount of memory allocated to workbuf */
2673 buflen
= 5120; /* Default size */
2674 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2675 wbptr
= workbuf
; /* start filling here */
2677 while (poslt(top
, bottom
)) {
2679 unsigned long *ldata
= lineptr(top
.y
);
2685 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2686 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2687 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2688 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2689 && poslt(top
, nlpos
))
2691 if (poslt(nlpos
, bottom
))
2694 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2697 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2699 wchar_t cbuf
[16], *p
;
2700 int uc
= (ldata
[top
.x
] & 0xFFFF);
2703 if (uc
== UCSWIDE
) {
2708 switch (uc
& CSET_MASK
) {
2711 uc
= unitab_xterm
[uc
& 0xFF];
2715 uc
= unitab_line
[uc
& 0xFF];
2718 switch (uc
& CSET_MASK
) {
2720 uc
= unitab_font
[uc
& 0xFF];
2723 uc
= unitab_oemcp
[uc
& 0xFF];
2727 set
= (uc
& CSET_MASK
);
2728 c
= (uc
& CHAR_MASK
);
2732 if (DIRECT_FONT(uc
)) {
2733 if (c
>= ' ' && c
!= 0x7F) {
2734 unsigned char buf
[4];
2737 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
2739 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
2740 rv
= MultiByteToWideChar(font_codepage
,
2741 0, buf
, 2, wbuf
, 4);
2745 rv
= MultiByteToWideChar(font_codepage
,
2746 0, buf
, 1, wbuf
, 4);
2750 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
2757 for (p
= cbuf
; *p
; p
++) {
2758 /* Enough overhead for trailing NL and nul */
2759 if (wblen
>= buflen
- 16) {
2762 sizeof(wchar_t) * (buflen
+= 100));
2763 wbptr
= workbuf
+ wblen
;
2772 for (i
= 0; i
< sel_nl_sz
; i
++) {
2774 *wbptr
++ = sel_nl
[i
];
2782 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
2783 if (buflen
> 0) /* indicates we allocated this buffer */
2787 void term_copyall(void)
2790 top
.y
= -count234(scrollback
);
2796 * The wordness array is mainly for deciding the disposition of the US-ASCII
2799 static int wordtype(int uc
)
2802 int start
, end
, ctype
;
2803 } *wptr
, ucs_words
[] = {
2809 0x037e, 0x037e, 1}, /* Greek question mark */
2811 0x0387, 0x0387, 1}, /* Greek ano teleia */
2813 0x055a, 0x055f, 1}, /* Armenian punctuation */
2815 0x0589, 0x0589, 1}, /* Armenian full stop */
2817 0x0700, 0x070d, 1}, /* Syriac punctuation */
2819 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2821 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2823 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2825 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2827 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2829 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2831 0x2000, 0x200a, 0}, /* Various spaces */
2833 0x2070, 0x207f, 2}, /* superscript */
2835 0x2080, 0x208f, 2}, /* subscript */
2837 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2839 0x3000, 0x3000, 0}, /* ideographic space */
2841 0x3001, 0x3020, 1}, /* ideographic punctuation */
2843 0x303f, 0x309f, 3}, /* Hiragana */
2845 0x30a0, 0x30ff, 3}, /* Katakana */
2847 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2849 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2851 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2853 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2855 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2857 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2859 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2861 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2863 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2868 uc
&= (CSET_MASK
| CHAR_MASK
);
2870 switch (uc
& CSET_MASK
) {
2872 uc
= unitab_xterm
[uc
& 0xFF];
2875 uc
= unitab_line
[uc
& 0xFF];
2878 switch (uc
& CSET_MASK
) {
2880 uc
= unitab_font
[uc
& 0xFF];
2883 uc
= unitab_oemcp
[uc
& 0xFF];
2888 return wordness
[uc
];
2890 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
2891 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
2899 * Spread the selection outwards according to the selection mode.
2901 static pos
sel_spread_half(pos p
, int dir
)
2903 unsigned long *ldata
;
2906 ldata
= lineptr(p
.y
);
2911 * In this mode, every character is a separate unit, except
2912 * for runs of spaces at the end of a non-wrapping line.
2914 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2915 unsigned long *q
= ldata
+ cols
;
2916 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
2918 if (q
== ldata
+ cols
)
2920 if (p
.x
>= q
- ldata
)
2921 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
2926 * In this mode, the units are maximal runs of characters
2927 * whose `wordness' has the same value.
2929 wvalue
= wordtype(ldata
[p
.x
]);
2931 while (p
.x
< cols
&& wordtype(ldata
[p
.x
+ 1]) == wvalue
)
2934 while (p
.x
> 0 && wordtype(ldata
[p
.x
- 1]) == wvalue
)
2940 * In this mode, every line is a unit.
2942 p
.x
= (dir
== -1 ?
0 : cols
- 1);
2948 static void sel_spread(void)
2950 selstart
= sel_spread_half(selstart
, -1);
2952 selend
= sel_spread_half(selend
, +1);
2956 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
2957 int shift
, int ctrl
)
2960 unsigned long *ldata
;
2976 selpoint
.y
= y
+ disptop
;
2978 ldata
= lineptr(selpoint
.y
);
2979 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
2983 int encstate
= 0, r
, c
;
2985 static int is_down
= 0;
2989 encstate
= 0x20; /* left button down */
3000 case MBT_WHEEL_DOWN
:
3003 default: break; /* placate gcc warning about enum use */
3007 if (xterm_mouse
== 1)
3020 default: break; /* placate gcc warning about enum use */
3029 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3030 ldisc_send(abuf
, 6);
3034 b
= translate_button(b
);
3036 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3038 selstate
= ABOUT_TO
;
3039 selanchor
= selpoint
;
3041 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3043 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3044 selstate
= DRAGGING
;
3045 selstart
= selanchor
= selpoint
;
3049 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3050 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3051 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3053 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3054 if (posdiff(selpoint
, selstart
) <
3055 posdiff(selend
, selstart
) / 2) {
3059 selanchor
= selstart
;
3061 selstate
= DRAGGING
;
3063 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3064 selanchor
= selpoint
;
3065 selstate
= DRAGGING
;
3066 if (poslt(selpoint
, selanchor
)) {
3067 selstart
= selpoint
;
3071 selstart
= selanchor
;
3076 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3077 if (selstate
== DRAGGING
) {
3079 * We've completed a selection. We now transfer the
3080 * data to the clipboard.
3082 clipme(selstart
, selend
);
3083 selstate
= SELECTED
;
3085 selstate
= NO_SELECTION
;
3086 } else if (b
== MBT_PASTE
3087 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3091 get_clip(&data
, &len
);
3096 sfree(paste_buffer
);
3097 paste_pos
= paste_hold
= paste_len
= 0;
3098 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3101 while (p
< data
+ len
) {
3102 while (p
< data
+ len
&&
3103 !(p
<= data
+ len
- sel_nl_sz
&&
3104 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3109 for (i
= 0; i
< p
- q
; i
++) {
3110 paste_buffer
[paste_len
++] = q
[i
];
3114 if (p
<= data
+ len
- sel_nl_sz
&&
3115 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3116 paste_buffer
[paste_len
++] = '\r';
3122 /* Assume a small paste will be OK in one go. */
3123 if (paste_len
< 256) {
3124 luni_send(paste_buffer
, paste_len
);
3126 sfree(paste_buffer
);
3128 paste_pos
= paste_hold
= paste_len
= 0;
3131 get_clip(NULL
, NULL
);
3141 sfree(paste_buffer
);
3148 static long last_paste
= 0;
3149 long now
, paste_diff
;
3154 /* Don't wait forever to paste */
3156 now
= GetTickCount();
3157 paste_diff
= now
- last_paste
;
3158 if (paste_diff
>= 0 && paste_diff
< 450)
3163 while (paste_pos
< paste_len
) {
3165 while (n
+ paste_pos
< paste_len
) {
3166 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3169 luni_send(paste_buffer
+ paste_pos
, n
);
3172 if (paste_pos
< paste_len
) {
3177 sfree(paste_buffer
);
3182 static void deselect(void)
3184 selstate
= NO_SELECTION
;
3185 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3188 void term_deselect(void)
3194 int term_ldisc(int option
)
3196 if (option
== LD_ECHO
)
3197 return term_echoing
;
3198 if (option
== LD_EDIT
)
3199 return term_editing
;
3204 * from_backend(), to get data from the backend for the terminal.
3206 void from_backend(int is_stderr
, char *data
, int len
)
3209 if (inbuf_head
>= INBUF_SIZE
)
3211 inbuf
[inbuf_head
++] = *data
++;
3216 * Log session traffic.
3218 void logtraffic(unsigned char c
, int logmode
)
3220 if (cfg
.logtype
> 0) {
3221 if (cfg
.logtype
== logmode
) {
3222 /* deferred open file from pgm start? */
3231 /* open log file append/overwrite mode */
3241 sprintf(writemod
, "wb"); /* default to rewrite */
3242 lgfp
= fopen(cfg
.logfilename
, "r"); /* file already present? */
3246 i
= askappend(cfg
.logfilename
);
3248 writemod
[0] = 'a'; /* set append mode */
3249 else if (i
== 0) { /* cancelled */
3251 cfg
.logtype
= 0; /* disable logging */
3256 lgfp
= fopen(cfg
.logfilename
, writemod
);
3257 if (lgfp
) { /* enter into event log */
3258 sprintf(buf
, "%s session log (%s mode) to file : ",
3259 (writemod
[0] == 'a') ?
"Appending" : "Writing new",
3260 (cfg
.logtype
== LGTYP_ASCII ?
"ASCII" :
3261 cfg
.logtype
== LGTYP_DEBUG ?
"raw" : "<ukwn>"));
3262 /* Make sure we do not exceed the output buffer size */
3263 strncat(buf
, cfg
.logfilename
, 128);
3264 buf
[strlen(buf
)] = '\0';
3267 /* --- write header line iinto log file */
3268 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp
);
3271 strftime(buf
, 24, "%Y.%m.%d %H:%M:%S", tm
);
3273 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp
);
3277 void logfclose(void)