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 /* Product-order comparisons for rectangular block selection. */
90 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
91 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
93 static bufchain inbuf
; /* terminal input buffer */
94 static pos curs
; /* cursor */
95 static pos savecurs
; /* saved cursor position */
96 static int marg_t
, marg_b
; /* scroll margins */
97 static int dec_om
; /* DEC origin mode flag */
98 static int wrap
, wrapnext
; /* wrap flags */
99 static int insert
; /* insert-mode flag */
100 static int cset
; /* 0 or 1: which char set */
101 static int save_cset
, save_csattr
; /* saved with cursor position */
102 static int save_utf
, save_wnext
; /* saved with cursor position */
103 static int rvideo
; /* global reverse video flag */
104 static unsigned long rvbell_startpoint
;/* for ESC[?5hESC[?5l vbell */
105 static int cursor_on
; /* cursor enabled flag */
106 static int reset_132
; /* Flag ESC c resets to 80 cols */
107 static int use_bce
; /* Use Background coloured erase */
108 static int blinker
; /* When blinking is the cursor on ? */
109 static int tblinker
; /* When the blinking text is on */
110 static int blink_is_real
; /* Actually blink blinking text */
111 static int term_echoing
; /* Does terminal want local echo? */
112 static int term_editing
; /* Does terminal want local edit? */
113 static int sco_acs
, save_sco_acs
; /* CSI 10,11,12m -> OEM charset */
114 static int vt52_bold
; /* Force bold on non-bold colours */
115 static int utf_state
; /* Is there a pending UTF-8 character */
116 static int utf_char
; /* and what is it so far. */
117 static int utf_size
; /* The size of the UTF character. */
119 static int xterm_mouse
; /* send mouse messages to app */
121 static unsigned long cset_attr
[2];
124 * Saved settings on the alternate screen.
126 static int alt_x
, alt_y
, alt_om
, alt_wrap
, alt_wnext
, alt_ins
, alt_cset
, alt_sco_acs
, alt_utf
;
127 static int alt_t
, alt_b
;
128 static int alt_which
;
130 #define ARGS_MAX 32 /* max # of esc sequence arguments */
131 #define ARG_DEFAULT 0 /* if an arg isn't specified */
132 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
133 static int esc_args
[ARGS_MAX
];
134 static int esc_nargs
;
135 static int esc_query
;
136 #define ANSI(x,y) ((x)+((y)<<8))
137 #define ANSI_QUE(x) ANSI(x,TRUE)
139 #define OSC_STR_MAX 2048
140 static int osc_strlen
;
141 static char osc_string
[OSC_STR_MAX
+ 1];
144 static char id_string
[1024] = "\033[?6c";
146 static unsigned char *tabs
;
158 OSC_STRING
, OSC_MAYBE_ST
,
167 NO_SELECTION
, ABOUT_TO
, DRAGGING
, SELECTED
170 LEXICOGRAPHIC
, RECTANGULAR
173 SM_CHAR
, SM_WORD
, SM_LINE
175 static pos selstart
, selend
, selanchor
;
177 static short wordness
[256] = {
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
180 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
181 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
182 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
183 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
184 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
185 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
186 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
187 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
189 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
190 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
191 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
192 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
193 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
196 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
197 static wchar_t sel_nl
[] = SEL_NL
;
198 static wchar_t *paste_buffer
= 0;
199 static int paste_len
, paste_pos
, paste_hold
;
202 * Internal prototypes.
204 static void do_paint(Context
, int);
205 static void erase_lots(int, int, int);
206 static void swap_screen(int);
207 static void update_sbar(void);
208 static void deselect(void);
211 * Resize a line to make it `cols' columns wide.
213 unsigned long *resizeline(unsigned long *line
, int cols
)
216 unsigned long lineattrs
;
218 if (line
[0] != (unsigned long)cols
) {
220 * This line is the wrong length, which probably means it
221 * hasn't been accessed since a resize. Resize it now.
224 lineattrs
= line
[oldlen
+ 1];
225 line
= srealloc(line
, TSIZE
* (2 + cols
));
227 for (i
= oldlen
; i
< cols
; i
++)
228 line
[i
+ 1] = ERASE_CHAR
;
229 line
[cols
+ 1] = lineattrs
& LATTR_MODE
;
236 * Retrieve a line of the screen or of the scrollback, according to
237 * whether the y coordinate is non-negative or negative
240 unsigned long *lineptr(int y
, int lineno
)
242 unsigned long *line
, *newline
;
250 whichtree
= scrollback
;
251 treeindex
= y
+ count234(scrollback
);
253 line
= index234(whichtree
, treeindex
);
255 /* We assume that we don't screw up and retrieve something out of range. */
256 assert(line
!= NULL
);
258 newline
= resizeline(line
, cols
);
259 if (newline
!= line
) {
260 delpos234(whichtree
, treeindex
);
261 addpos234(whichtree
, newline
, treeindex
);
268 #define lineptr(x) lineptr(x,__LINE__)
270 * Set up power-on settings for the terminal.
272 static void power_on(void)
274 curs
.x
= curs
.y
= alt_x
= alt_y
= savecurs
.x
= savecurs
.y
= 0;
277 alt_b
= marg_b
= rows
- 1;
282 for (i
= 0; i
< cols
; i
++)
283 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
285 alt_om
= dec_om
= cfg
.dec_om
;
286 alt_wnext
= wrapnext
= alt_ins
= insert
= FALSE
;
287 alt_wrap
= wrap
= cfg
.wrap_mode
;
290 alt_sco_acs
= sco_acs
= 0;
291 cset_attr
[0] = cset_attr
[1] = ATTR_ASCII
;
296 save_attr
= curr_attr
= ATTR_DEFAULT
;
297 term_editing
= term_echoing
= FALSE
;
298 ldisc_send(NULL
, 0, 0); /* cause ldisc to notice changes */
299 app_cursor_keys
= cfg
.app_cursor
;
300 app_keypad_keys
= cfg
.app_keypad
;
302 blink_is_real
= cfg
.blinktext
;
303 erase_char
= ERASE_CHAR
;
307 for (i
= 0; i
< 256; i
++)
308 wordness
[i
] = cfg
.wordness
[i
];
312 erase_lots(FALSE
, TRUE
, TRUE
);
314 erase_lots(FALSE
, TRUE
, TRUE
);
319 * Force a screen update.
321 void term_update(void)
326 int need_sbar_update
= seen_disp_event
;
327 if ((seen_key_event
&& (cfg
.scroll_on_key
)) ||
328 (seen_disp_event
&& (cfg
.scroll_on_disp
))) {
329 disptop
= 0; /* return to main screen */
330 seen_disp_event
= seen_key_event
= 0;
331 need_sbar_update
= TRUE
;
333 if (need_sbar_update
)
336 sys_cursor(curs
.x
, curs
.y
- disptop
);
342 * Same as power_on(), but an external function.
344 void term_pwron(void)
354 * Clear the scrollback.
356 void term_clrsb(void)
360 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
367 * Initialise the terminal.
371 screen
= alt_screen
= scrollback
= NULL
;
373 disptext
= dispcurs
= NULL
;
378 beephead
= beeptail
= NULL
;
381 beep_overloaded
= FALSE
;
385 * Set up the terminal for a given size.
387 void term_size(int newrows
, int newcols
, int newsavelines
)
390 unsigned long *newdisp
, *line
;
393 int save_alt_which
= alt_which
;
395 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
396 return; /* nothing to do */
402 alt_b
= marg_b
= newrows
- 1;
405 scrollback
= newtree234(NULL
);
406 screen
= newtree234(NULL
);
411 * Resize the screen and scrollback. We only need to shift
412 * lines around within our data structures, because lineptr()
413 * will take care of resizing each individual line if
416 * - If the new screen and the old screen differ in length, we
417 * must shunt some lines in from the scrollback or out to
420 * - If doing that fails to provide us with enough material to
421 * fill the new screen (i.e. the number of rows needed in
422 * the new screen exceeds the total number in the previous
423 * screen+scrollback), we must invent some blank lines to
426 * - Then, if the new scrollback length is less than the
427 * amount of scrollback we actually have, we must throw some
430 sblen
= count234(scrollback
);
431 /* Do this loop to expand the screen if newrows > rows */
432 for (i
= rows
; i
< newrows
; i
++) {
434 line
= delpos234(scrollback
, --sblen
);
436 line
= smalloc(TSIZE
* (newcols
+ 2));
438 for (j
= 0; j
<= newcols
; j
++)
439 line
[j
+ 1] = ERASE_CHAR
;
441 addpos234(screen
, line
, 0);
443 /* Do this loop to shrink the screen if newrows < rows */
444 for (i
= newrows
; i
< rows
; i
++) {
445 line
= delpos234(screen
, 0);
446 addpos234(scrollback
, line
, sblen
++);
448 assert(count234(screen
) == newrows
);
449 while (sblen
> newsavelines
) {
450 line
= delpos234(scrollback
, 0);
454 assert(count234(scrollback
) <= newsavelines
);
457 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
458 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
459 newdisp
[i
] = ATTR_INVALID
;
464 newalt
= newtree234(NULL
);
465 for (i
= 0; i
< newrows
; i
++) {
466 line
= smalloc(TSIZE
* (newcols
+ 2));
468 for (j
= 0; j
<= newcols
; j
++)
469 line
[j
+ 1] = erase_char
;
470 addpos234(newalt
, line
, i
);
473 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
475 freetree234(alt_screen
);
479 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
482 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
483 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
487 curs
.y
+= newrows
- rows
;
490 if (curs
.y
>= newrows
)
491 curs
.y
= newrows
- 1;
492 if (curs
.x
>= newcols
)
493 curs
.x
= newcols
- 1;
495 wrapnext
= alt_wnext
= FALSE
;
499 savelines
= newsavelines
;
502 swap_screen(save_alt_which
);
512 static void swap_screen(int which
)
517 if (which
== alt_which
)
544 wrapnext
= alt_wnext
;
556 sco_acs
= alt_sco_acs
;
563 * Update the scroll bar.
565 static void update_sbar(void)
569 nscroll
= count234(scrollback
);
571 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
575 * Check whether the region bounded by the two pointers intersects
576 * the scroll region, and de-select the on-screen selection if so.
578 static void check_selection(pos from
, pos to
)
580 if (poslt(from
, selend
) && poslt(selstart
, to
))
585 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
586 * for backward.) `sb' is TRUE if the scrolling is permitted to
587 * affect the scrollback buffer.
589 * NB this function invalidates all pointers into lines of the
590 * screen data structures. In particular, you MUST call fix_cpos
591 * after calling scroll() and before doing anything else that
592 * uses the cpos shortcut pointer.
594 static void scroll(int topline
, int botline
, int lines
, int sb
)
596 unsigned long *line
, *line2
;
599 if (topline
!= 0 || alt_which
!= 0)
604 line
= delpos234(screen
, botline
);
605 line
= resizeline(line
, cols
);
606 for (i
= 0; i
< cols
; i
++)
607 line
[i
+ 1] = erase_char
;
609 addpos234(screen
, line
, topline
);
611 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
613 if (selstart
.y
> botline
) {
614 selstart
.y
= botline
;
618 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
620 if (selend
.y
> botline
) {
630 line
= delpos234(screen
, topline
);
631 if (sb
&& savelines
> 0) {
632 int sblen
= count234(scrollback
);
634 * We must add this line to the scrollback. We'll
635 * remove a line from the top of the scrollback to
636 * replace it, or allocate a new one if the
637 * scrollback isn't full.
639 if (sblen
== savelines
) {
640 sblen
--, line2
= delpos234(scrollback
, 0);
642 line2
= smalloc(TSIZE
* (cols
+ 2));
645 addpos234(scrollback
, line
, sblen
);
649 * If the user is currently looking at part of the
650 * scrollback, and they haven't enabled any options
651 * that are going to reset the scrollback as a
652 * result of this movement, then the chances are
653 * they'd like to keep looking at the same line. So
654 * we move their viewpoint at the same rate as the
655 * scroll, at least until their viewpoint hits the
656 * top end of the scrollback buffer, at which point
657 * we don't have the choice any more.
659 * Thanks to Jan Holmen Holsten for the idea and
660 * initial implementation.
662 if (disptop
> -savelines
&& disptop
< 0)
665 line
= resizeline(line
, cols
);
666 for (i
= 0; i
< cols
; i
++)
667 line
[i
+ 1] = erase_char
;
669 addpos234(screen
, line
, botline
);
672 * If the selection endpoints move into the scrollback,
673 * we keep them moving until they hit the top. However,
674 * of course, if the line _hasn't_ moved into the
675 * scrollback then we don't do this, and cut them off
676 * at the top of the scroll region.
678 * This applies to selstart and selend (for an existing
679 * selection), and also selanchor (for one being
680 * selected as we speak).
682 seltop
= sb ?
-savelines
: topline
;
684 if (selstart
.y
>= seltop
&& selstart
.y
<= botline
) {
686 if (selstart
.y
< seltop
) {
691 if (selend
.y
>= seltop
&& selend
.y
<= botline
) {
693 if (selend
.y
< seltop
) {
698 if (selanchor
.y
>= seltop
&& selanchor
.y
<= botline
) {
700 if (selanchor
.y
< seltop
) {
701 selanchor
.y
= seltop
;
712 * Move the cursor to a given position, clipping at boundaries. We
713 * may or may not want to clip at the scroll margin: marg_clip is 0
714 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
715 * even _being_ outside the margins.
717 static void move(int x
, int y
, int marg_clip
)
724 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
726 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
740 * Save or restore the cursor and SGR mode.
742 static void save_cursor(int save
)
746 save_attr
= curr_attr
;
749 save_wnext
= wrapnext
;
750 save_csattr
= cset_attr
[cset
];
751 save_sco_acs
= sco_acs
;
754 /* Make sure the window hasn't shrunk since the save */
760 curr_attr
= save_attr
;
763 wrapnext
= save_wnext
;
765 * wrapnext might reset to False if the x position is no
766 * longer at the rightmost edge.
768 if (wrapnext
&& curs
.x
< cols
-1)
770 cset_attr
[cset
] = save_csattr
;
771 sco_acs
= save_sco_acs
;
774 erase_char
= (' ' | ATTR_ASCII
|
775 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
780 * Erase a large portion of the screen: the whole screen, or the
781 * whole line, or parts thereof.
783 static void erase_lots(int line_only
, int from_begin
, int to_end
)
787 unsigned long *ldata
;
809 check_selection(start
, end
);
811 /* Clear screen also forces a full window redraw, just in case. */
812 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
815 ldata
= lineptr(start
.y
);
816 while (poslt(start
, end
)) {
817 if (start
.x
== cols
&& !erase_lattr
)
818 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
820 ldata
[start
.x
] = erase_char
;
821 if (incpos(start
) && start
.y
< rows
)
822 ldata
= lineptr(start
.y
);
827 * Insert or delete characters within the current line. n is +ve if
828 * insertion is desired, and -ve for deletion.
830 static void insch(int n
)
832 int dir
= (n
< 0 ?
-1 : +1);
835 unsigned long *ldata
;
837 n
= (n
< 0 ?
-n
: n
);
838 if (n
> cols
- curs
.x
)
840 m
= cols
- curs
.x
- n
;
842 cursplus
.x
= curs
.x
+ n
;
843 check_selection(curs
, cursplus
);
844 ldata
= lineptr(curs
.y
);
846 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
848 ldata
[curs
.x
+ m
++] = erase_char
;
850 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
852 ldata
[curs
.x
+ n
] = erase_char
;
857 * Toggle terminal mode `mode' to state `state'. (`query' indicates
858 * whether the mode is a DEC private one or a normal one.)
860 static void toggle_mode(int mode
, int query
, int state
)
866 case 1: /* application cursor keys */
867 app_cursor_keys
= state
;
869 case 2: /* VT52 mode */
872 blink_is_real
= FALSE
;
875 blink_is_real
= cfg
.blinktext
;
878 case 3: /* 80/132 columns */
880 request_resize(state ?
132 : 80, rows
);
883 case 5: /* reverse video */
885 * Toggle reverse video. If we receive an OFF within the
886 * visual bell timeout period after an ON, we trigger an
887 * effective visual bell, so that ESC[?5hESC[?5l will
888 * always be an actually _visible_ visual bell.
890 ticks
= GetTickCount();
891 /* turn off a previous vbell to avoid inconsistencies */
892 if (ticks
- vbell_startpoint
>= VBELL_TIMEOUT
)
894 if (rvideo
&& !state
&& /* we're turning it off... */
895 (ticks
- rvbell_startpoint
) < VBELL_TIMEOUT
) { /* ...soon */
896 /* If there's no vbell timeout already, or this one lasts
897 * longer, replace vbell_timeout with ours. */
899 (rvbell_startpoint
- vbell_startpoint
< VBELL_TIMEOUT
))
900 vbell_startpoint
= rvbell_startpoint
;
901 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
902 } else if (!rvideo
&& state
) {
903 /* This is an ON, so we notice the time and save it. */
904 rvbell_startpoint
= ticks
;
907 seen_disp_event
= TRUE
;
911 case 6: /* DEC origin mode */
914 case 7: /* auto wrap */
917 case 8: /* auto key repeat */
920 case 10: /* set local edit mode */
921 term_editing
= state
;
922 ldisc_send(NULL
, 0, 0); /* cause ldisc to notice changes */
924 case 25: /* enable/disable cursor */
925 compatibility2(OTHER
, VT220
);
927 seen_disp_event
= TRUE
;
929 case 47: /* alternate screen */
930 compatibility(OTHER
);
935 case 1000: /* xterm mouse 1 */
936 xterm_mouse
= state ?
1 : 0;
937 set_raw_mouse_mode(state
);
939 case 1002: /* xterm mouse 2 */
940 xterm_mouse
= state ?
2 : 0;
941 set_raw_mouse_mode(state
);
945 case 4: /* set insert mode */
946 compatibility(VT102
);
949 case 12: /* set echo mode */
950 term_echoing
= !state
;
951 ldisc_send(NULL
, 0, 0); /* cause ldisc to notice changes */
953 case 20: /* Return sends ... */
954 cr_lf_return
= state
;
956 case 34: /* Make cursor BIG */
957 compatibility2(OTHER
, VT220
);
963 * Process an OSC sequence: set window title or icon name.
965 static void do_osc(void)
969 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
971 osc_string
[osc_strlen
] = '\0';
972 switch (esc_args
[0]) {
975 set_icon(osc_string
);
976 if (esc_args
[0] == 1)
978 /* fall through: parameter 0 means set both */
981 set_title(osc_string
);
988 * Remove everything currently in `inbuf' and stick it up on the
989 * in-memory display. There's a big state machine in here to
990 * process escape sequences...
995 unsigned char localbuf
[256], *chars
;
1000 while (nchars
> 0 || bufchain_size(&inbuf
) > 0) {
1004 bufchain_prefix(&inbuf
, &ret
, &nchars
);
1005 if (nchars
> sizeof(localbuf
))
1006 nchars
= sizeof(localbuf
);
1007 memcpy(localbuf
, ret
, nchars
);
1008 bufchain_consume(&inbuf
, nchars
);
1010 assert(chars
!= NULL
);
1016 * Optionally log the session traffic to a file. Useful for
1017 * debugging and possibly also useful for actual logging.
1019 if (cfg
.logtype
== LGTYP_DEBUG
)
1020 logtraffic((unsigned char) c
, LGTYP_DEBUG
);
1026 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1027 * be able to display 8-bit characters, but I'll let that go 'cause
1031 /* First see about all those translations. */
1032 if (termstate
== TOPLEVEL
) {
1034 switch (utf_state
) {
1037 /* UTF-8 must be stateless so we ignore iso2022. */
1038 if (unitab_ctrl
[c
] != 0xFF)
1040 else c
= ((unsigned char)c
) | ATTR_ASCII
;
1042 } else if ((c
& 0xe0) == 0xc0) {
1043 utf_size
= utf_state
= 1;
1044 utf_char
= (c
& 0x1f);
1045 } else if ((c
& 0xf0) == 0xe0) {
1046 utf_size
= utf_state
= 2;
1047 utf_char
= (c
& 0x0f);
1048 } else if ((c
& 0xf8) == 0xf0) {
1049 utf_size
= utf_state
= 3;
1050 utf_char
= (c
& 0x07);
1051 } else if ((c
& 0xfc) == 0xf8) {
1052 utf_size
= utf_state
= 4;
1053 utf_char
= (c
& 0x03);
1054 } else if ((c
& 0xfe) == 0xfc) {
1055 utf_size
= utf_state
= 5;
1056 utf_char
= (c
& 0x01);
1067 if ((c
& 0xC0) != 0x80) {
1073 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
1079 /* Is somebody trying to be evil! */
1081 (c
< 0x800 && utf_size
>= 2) ||
1082 (c
< 0x10000 && utf_size
>= 3) ||
1083 (c
< 0x200000 && utf_size
>= 4) ||
1084 (c
< 0x4000000 && utf_size
>= 5))
1087 /* Unicode line separator and paragraph separator are CR-LF */
1088 if (c
== 0x2028 || c
== 0x2029)
1091 /* High controls are probably a Baaad idea too. */
1095 /* The UTF-16 surrogates are not nice either. */
1096 /* The standard give the option of decoding these:
1097 * I don't want to! */
1098 if (c
>= 0xD800 && c
< 0xE000)
1101 /* ISO 10646 characters now limited to UTF-16 range. */
1105 /* This is currently a TagPhobic application.. */
1106 if (c
>= 0xE0000 && c
<= 0xE007F)
1109 /* U+FEFF is best seen as a null. */
1112 /* But U+FFFE is an error. */
1113 if (c
== 0xFFFE || c
== 0xFFFF)
1116 /* Oops this is a 16bit implementation */
1121 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1123 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1125 if (sco_acs
== 2) c
^= 0x80;
1128 switch (cset_attr
[cset
]) {
1130 * Linedraw characters are different from 'ESC ( B'
1131 * only for a small range. For ones outside that
1132 * range, make sure we use the same font as well as
1133 * the same encoding.
1136 if (unitab_ctrl
[c
] != 0xFF)
1139 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1143 /* If UK-ASCII, make the '#' a LineDraw Pound */
1145 c
= '}' | ATTR_LINEDRW
;
1148 /*FALLTHROUGH*/ case ATTR_ASCII
:
1149 if (unitab_ctrl
[c
] != 0xFF)
1152 c
= ((unsigned char) c
) | ATTR_ASCII
;
1155 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1161 /* How about C1 controls ? */
1162 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1163 has_compat(VT220
)) {
1164 termstate
= SEEN_ESC
;
1166 c
= '@' + (c
& 0x1F);
1169 /* Or the GL control. */
1170 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1171 if (curs
.x
&& !wrapnext
)
1175 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1177 /* Or normal C0 controls. */
1178 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1180 case '\005': /* terminal type query */
1181 /* Strictly speaking this is VT100 but a VT100 defaults to
1182 * no response. Other terminals respond at their option.
1184 * Don't put a CR in the default string as this tends to
1185 * upset some weird software.
1187 * An xterm returns "xterm" (5 characters)
1189 compatibility(ANSIMIN
);
1191 char abuf
[256], *s
, *d
;
1193 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1195 if (*s
>= 'a' && *s
<= 'z')
1196 *d
++ = (*s
- ('a' - 1));
1197 else if ((*s
>= '@' && *s
<= '_') ||
1198 *s
== '?' || (*s
& 0x80))
1203 } else if (*s
== '^') {
1208 lpage_send(CP_ACP
, abuf
, d
- abuf
, 0);
1213 struct beeptime
*newbeep
;
1214 unsigned long ticks
;
1216 ticks
= GetTickCount();
1218 if (!beep_overloaded
) {
1219 newbeep
= smalloc(sizeof(struct beeptime
));
1220 newbeep
->ticks
= ticks
;
1221 newbeep
->next
= NULL
;
1225 beeptail
->next
= newbeep
;
1231 * Throw out any beeps that happened more than
1235 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1236 struct beeptime
*tmp
= beephead
;
1237 beephead
= tmp
->next
;
1244 if (cfg
.bellovl
&& beep_overloaded
&&
1245 ticks
- lastbeep
>= (unsigned)cfg
.bellovl_s
) {
1247 * If we're currently overloaded and the
1248 * last beep was more than s seconds ago,
1249 * leave overload mode.
1251 beep_overloaded
= FALSE
;
1252 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1253 nbeeps
>= cfg
.bellovl_n
) {
1255 * Now, if we have n or more beeps
1256 * remaining in the queue, go into overload
1259 beep_overloaded
= TRUE
;
1264 * Perform an actual beep if we're not overloaded.
1266 if (!cfg
.bellovl
|| !beep_overloaded
) {
1268 if (cfg
.beep
== BELL_VISUAL
) {
1270 vbell_startpoint
= ticks
;
1278 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1279 else if (curs
.x
== 0 && curs
.y
> 0)
1280 curs
.x
= cols
- 1, curs
.y
--;
1286 seen_disp_event
= TRUE
;
1289 compatibility(VT100
);
1293 compatibility(VT100
);
1298 termstate
= VT52_ESC
;
1300 compatibility(ANSIMIN
);
1301 termstate
= SEEN_ESC
;
1309 seen_disp_event
= TRUE
;
1311 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1314 if (has_compat(SCOANSI
)) {
1316 erase_lots(FALSE
, FALSE
, TRUE
);
1319 seen_disp_event
= 1;
1323 compatibility(VT100
);
1325 if (curs
.y
== marg_b
)
1326 scroll(marg_t
, marg_b
, 1, TRUE
);
1327 else if (curs
.y
< rows
- 1)
1333 seen_disp_event
= 1;
1335 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1339 pos old_curs
= curs
;
1340 unsigned long *ldata
= lineptr(curs
.y
);
1344 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1346 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1347 if (curs
.x
>= cols
/ 2)
1348 curs
.x
= cols
/ 2 - 1;
1355 check_selection(old_curs
, curs
);
1357 seen_disp_event
= TRUE
;
1361 switch (termstate
) {
1363 /* Only graphic characters get this far, ctrls are stripped above */
1364 if (wrapnext
&& wrap
) {
1365 cpos
[1] |= LATTR_WRAPPED
;
1366 if (curs
.y
== marg_b
)
1367 scroll(marg_t
, marg_b
, 1, TRUE
);
1368 else if (curs
.y
< rows
- 1)
1376 if (selstate
!= NO_SELECTION
) {
1377 pos cursplus
= curs
;
1379 check_selection(curs
, cursplus
);
1381 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1382 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1384 extern int wcwidth(wchar_t ucs
);
1389 width
= wcwidth((wchar_t) c
);
1392 *cpos
++ = c
| curr_attr
;
1393 if (++curs
.x
== cols
) {
1394 *cpos
|= LATTR_WRAPPED
;
1395 if (curs
.y
== marg_b
)
1396 scroll(marg_t
, marg_b
, 1, TRUE
);
1397 else if (curs
.y
< rows
- 1)
1402 *cpos
++ = UCSWIDE
| curr_attr
;
1405 *cpos
++ = c
| curr_attr
;
1412 if (curs
.x
== cols
) {
1416 if (wrap
&& vt52_mode
) {
1417 cpos
[1] |= LATTR_WRAPPED
;
1418 if (curs
.y
== marg_b
)
1419 scroll(marg_t
, marg_b
, 1, TRUE
);
1420 else if (curs
.y
< rows
- 1)
1427 seen_disp_event
= 1;
1432 * This state is virtually identical to SEEN_ESC, with the
1433 * exception that we have an OSC sequence in the pipeline,
1434 * and _if_ we see a backslash, we process it.
1438 termstate
= TOPLEVEL
;
1441 /* else fall through */
1443 if (c
>= ' ' && c
<= '/') {
1450 termstate
= TOPLEVEL
;
1451 switch (ANSI(c
, esc_query
)) {
1452 case '[': /* enter CSI mode */
1453 termstate
= SEEN_CSI
;
1455 esc_args
[0] = ARG_DEFAULT
;
1458 case ']': /* xterm escape sequences */
1459 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1460 compatibility(OTHER
);
1461 termstate
= SEEN_OSC
;
1464 case '7': /* save cursor */
1465 compatibility(VT100
);
1468 case '8': /* restore cursor */
1469 compatibility(VT100
);
1471 seen_disp_event
= TRUE
;
1474 compatibility(VT100
);
1475 app_keypad_keys
= TRUE
;
1478 compatibility(VT100
);
1479 app_keypad_keys
= FALSE
;
1481 case 'D': /* exactly equivalent to LF */
1482 compatibility(VT100
);
1483 if (curs
.y
== marg_b
)
1484 scroll(marg_t
, marg_b
, 1, TRUE
);
1485 else if (curs
.y
< rows
- 1)
1489 seen_disp_event
= TRUE
;
1491 case 'E': /* exactly equivalent to CR-LF */
1492 compatibility(VT100
);
1494 if (curs
.y
== marg_b
)
1495 scroll(marg_t
, marg_b
, 1, TRUE
);
1496 else if (curs
.y
< rows
- 1)
1500 seen_disp_event
= TRUE
;
1502 case 'M': /* reverse index - backwards LF */
1503 compatibility(VT100
);
1504 if (curs
.y
== marg_t
)
1505 scroll(marg_t
, marg_b
, -1, TRUE
);
1506 else if (curs
.y
> 0)
1510 seen_disp_event
= TRUE
;
1512 case 'Z': /* terminal type query */
1513 compatibility(VT100
);
1514 ldisc_send(id_string
, strlen(id_string
), 0);
1516 case 'c': /* restore power-on settings */
1517 compatibility(VT100
);
1520 request_resize(80, rows
);
1525 seen_disp_event
= TRUE
;
1527 case 'H': /* set a tab */
1528 compatibility(VT100
);
1529 tabs
[curs
.x
] = TRUE
;
1532 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1533 compatibility(VT100
);
1535 unsigned long *ldata
;
1539 for (i
= 0; i
< rows
; i
++) {
1541 for (j
= 0; j
< cols
; j
++)
1542 ldata
[j
] = ATTR_DEFAULT
| 'E';
1546 seen_disp_event
= TRUE
;
1547 scrtop
.x
= scrtop
.y
= 0;
1550 check_selection(scrtop
, scrbot
);
1554 case ANSI('3', '#'):
1555 case ANSI('4', '#'):
1556 case ANSI('5', '#'):
1557 case ANSI('6', '#'):
1558 compatibility(VT100
);
1560 unsigned long nlattr
;
1561 unsigned long *ldata
;
1562 switch (ANSI(c
, esc_query
)) {
1563 case ANSI('3', '#'):
1566 case ANSI('4', '#'):
1569 case ANSI('5', '#'):
1570 nlattr
= LATTR_NORM
;
1572 default: /* spiritually case ANSI('6', '#'): */
1573 nlattr
= LATTR_WIDE
;
1576 ldata
= lineptr(curs
.y
);
1577 ldata
[cols
] &= ~LATTR_MODE
;
1578 ldata
[cols
] |= nlattr
;
1582 case ANSI('A', '('):
1583 compatibility(VT100
);
1584 cset_attr
[0] = ATTR_GBCHR
;
1586 case ANSI('B', '('):
1587 compatibility(VT100
);
1588 cset_attr
[0] = ATTR_ASCII
;
1590 case ANSI('0', '('):
1591 compatibility(VT100
);
1592 cset_attr
[0] = ATTR_LINEDRW
;
1594 case ANSI('U', '('):
1595 compatibility(OTHER
);
1596 cset_attr
[0] = ATTR_SCOACS
;
1599 case ANSI('A', ')'):
1600 compatibility(VT100
);
1601 cset_attr
[1] = ATTR_GBCHR
;
1603 case ANSI('B', ')'):
1604 compatibility(VT100
);
1605 cset_attr
[1] = ATTR_ASCII
;
1607 case ANSI('0', ')'):
1608 compatibility(VT100
);
1609 cset_attr
[1] = ATTR_LINEDRW
;
1611 case ANSI('U', ')'):
1612 compatibility(OTHER
);
1613 cset_attr
[1] = ATTR_SCOACS
;
1616 case ANSI('8', '%'): /* Old Linux code */
1617 case ANSI('G', '%'):
1618 compatibility(OTHER
);
1621 case ANSI('@', '%'):
1622 compatibility(OTHER
);
1628 termstate
= TOPLEVEL
; /* default */
1630 if (esc_nargs
<= ARGS_MAX
) {
1631 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1632 esc_args
[esc_nargs
- 1] = 0;
1633 esc_args
[esc_nargs
- 1] =
1634 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1636 termstate
= SEEN_CSI
;
1637 } else if (c
== ';') {
1638 if (++esc_nargs
<= ARGS_MAX
)
1639 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1640 termstate
= SEEN_CSI
;
1641 } else if (c
< '@') {
1648 termstate
= SEEN_CSI
;
1650 switch (ANSI(c
, esc_query
)) {
1651 case 'A': /* move up N lines */
1652 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1653 seen_disp_event
= TRUE
;
1655 case 'e': /* move down N lines */
1656 compatibility(ANSI
);
1659 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1660 seen_disp_event
= TRUE
;
1662 case ANSI('c', '>'): /* report xterm version */
1663 compatibility(OTHER
);
1664 /* this reports xterm version 136 so that VIM can
1665 use the drag messages from the mouse reporting */
1666 ldisc_send("\033[>0;136;0c", 11, 0);
1668 case 'a': /* move right N cols */
1669 compatibility(ANSI
);
1672 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1673 seen_disp_event
= TRUE
;
1675 case 'D': /* move left N cols */
1676 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1677 seen_disp_event
= TRUE
;
1679 case 'E': /* move down N lines and CR */
1680 compatibility(ANSI
);
1681 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1682 seen_disp_event
= TRUE
;
1684 case 'F': /* move up N lines and CR */
1685 compatibility(ANSI
);
1686 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1687 seen_disp_event
= TRUE
;
1690 case '`': /* set horizontal posn */
1691 compatibility(ANSI
);
1692 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1693 seen_disp_event
= TRUE
;
1695 case 'd': /* set vertical posn */
1696 compatibility(ANSI
);
1698 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1701 seen_disp_event
= TRUE
;
1704 case 'f': /* set horz and vert posns at once */
1706 esc_args
[1] = ARG_DEFAULT
;
1707 move(def(esc_args
[1], 1) - 1,
1708 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1711 seen_disp_event
= TRUE
;
1713 case 'J': /* erase screen or parts of it */
1715 unsigned int i
= def(esc_args
[0], 0) + 1;
1718 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1721 seen_disp_event
= TRUE
;
1723 case 'K': /* erase line or parts of it */
1725 unsigned int i
= def(esc_args
[0], 0) + 1;
1728 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1730 seen_disp_event
= TRUE
;
1732 case 'L': /* insert lines */
1733 compatibility(VT102
);
1734 if (curs
.y
<= marg_b
)
1735 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1738 seen_disp_event
= TRUE
;
1740 case 'M': /* delete lines */
1741 compatibility(VT102
);
1742 if (curs
.y
<= marg_b
)
1743 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1746 seen_disp_event
= TRUE
;
1748 case '@': /* insert chars */
1749 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1750 compatibility(VT102
);
1751 insch(def(esc_args
[0], 1));
1752 seen_disp_event
= TRUE
;
1754 case 'P': /* delete chars */
1755 compatibility(VT102
);
1756 insch(-def(esc_args
[0], 1));
1757 seen_disp_event
= TRUE
;
1759 case 'c': /* terminal type query */
1760 compatibility(VT100
);
1761 /* This is the response for a VT102 */
1762 ldisc_send(id_string
, strlen(id_string
), 0);
1764 case 'n': /* cursor position query */
1765 if (esc_args
[0] == 6) {
1767 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1769 ldisc_send(buf
, strlen(buf
), 0);
1770 } else if (esc_args
[0] == 5) {
1771 ldisc_send("\033[0n", 4, 0);
1774 case 'h': /* toggle modes to high */
1776 compatibility(VT100
);
1779 for (i
= 0; i
< esc_nargs
; i
++)
1780 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1783 case 'l': /* toggle modes to low */
1785 compatibility(VT100
);
1788 for (i
= 0; i
< esc_nargs
; i
++)
1789 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1792 case 'g': /* clear tabs */
1793 compatibility(VT100
);
1794 if (esc_nargs
== 1) {
1795 if (esc_args
[0] == 0) {
1796 tabs
[curs
.x
] = FALSE
;
1797 } else if (esc_args
[0] == 3) {
1799 for (i
= 0; i
< cols
; i
++)
1804 case 'r': /* set scroll margins */
1805 compatibility(VT100
);
1806 if (esc_nargs
<= 2) {
1808 top
= def(esc_args
[0], 1) - 1;
1809 bot
= (esc_nargs
<= 1
1811 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1814 /* VTTEST Bug 9 - if region is less than 2 lines
1815 * don't change region.
1817 if (bot
- top
> 0) {
1822 * I used to think the cursor should be
1823 * placed at the top of the newly marginned
1824 * area. Apparently not: VMS TPU falls over
1827 * Well actually it should for Origin mode - RDB
1829 curs
.y
= (dec_om ? marg_t
: 0);
1831 seen_disp_event
= TRUE
;
1835 case 'm': /* set graphics rendition */
1838 * A VT100 without the AVO only had one attribute, either
1839 * underline or reverse video depending on the cursor type,
1840 * this was selected by CSI 7m.
1843 * This is sometimes DIM, eg on the GIGI and Linux
1845 * This is sometimes INVIS various ANSI.
1847 * This like 22 disables BOLD, DIM and INVIS
1849 * The ANSI colours appear on any terminal that has colour
1850 * (obviously) but the interaction between sgr0 and the
1851 * colours varies but is usually related to the background
1852 * colour erase item.
1853 * The interaction between colour attributes and the mono
1854 * ones is also very implementation dependent.
1856 * The 39 and 49 attributes are likely to be unimplemented.
1859 for (i
= 0; i
< esc_nargs
; i
++) {
1860 switch (def(esc_args
[i
], 0)) {
1861 case 0: /* restore defaults */
1862 curr_attr
= ATTR_DEFAULT
;
1864 case 1: /* enable bold */
1865 compatibility(VT100AVO
);
1866 curr_attr
|= ATTR_BOLD
;
1868 case 21: /* (enable double underline) */
1869 compatibility(OTHER
);
1870 case 4: /* enable underline */
1871 compatibility(VT100AVO
);
1872 curr_attr
|= ATTR_UNDER
;
1874 case 5: /* enable blink */
1875 compatibility(VT100AVO
);
1876 curr_attr
|= ATTR_BLINK
;
1878 case 7: /* enable reverse video */
1879 curr_attr
|= ATTR_REVERSE
;
1881 case 10: /* SCO acs off */
1882 compatibility(SCOANSI
);
1884 case 11: /* SCO acs on */
1885 compatibility(SCOANSI
);
1887 case 12: /* SCO acs on flipped */
1888 compatibility(SCOANSI
);
1890 case 22: /* disable bold */
1891 compatibility2(OTHER
, VT220
);
1892 curr_attr
&= ~ATTR_BOLD
;
1894 case 24: /* disable underline */
1895 compatibility2(OTHER
, VT220
);
1896 curr_attr
&= ~ATTR_UNDER
;
1898 case 25: /* disable blink */
1899 compatibility2(OTHER
, VT220
);
1900 curr_attr
&= ~ATTR_BLINK
;
1902 case 27: /* disable reverse video */
1903 compatibility2(OTHER
, VT220
);
1904 curr_attr
&= ~ATTR_REVERSE
;
1915 curr_attr
&= ~ATTR_FGMASK
;
1917 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1919 case 39: /* default-foreground */
1920 curr_attr
&= ~ATTR_FGMASK
;
1921 curr_attr
|= ATTR_DEFFG
;
1932 curr_attr
&= ~ATTR_BGMASK
;
1934 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1936 case 49: /* default-background */
1937 curr_attr
&= ~ATTR_BGMASK
;
1938 curr_attr
|= ATTR_DEFBG
;
1943 erase_char
= (' ' | ATTR_ASCII
|
1945 (ATTR_FGMASK
| ATTR_BGMASK
)));
1948 case 's': /* save cursor */
1951 case 'u': /* restore cursor */
1953 seen_disp_event
= TRUE
;
1955 case 't': /* set page size - ie window height */
1957 * VT340/VT420 sequence DECSLPP, DEC only allows values
1958 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1959 * illegal values (eg first arg 1..9) for window changing
1963 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1964 compatibility(VT340TEXT
);
1965 request_resize(cols
, def(esc_args
[0], 24));
1967 } else if (esc_nargs
>= 1 &&
1970 compatibility(OTHER
);
1972 switch (esc_args
[0]) {
1982 if (esc_nargs
>= 3) {
1983 move_window(def(esc_args
[1], 0),
1984 def(esc_args
[2], 0));
1988 /* We should resize the window to a given
1989 * size in pixels here, but currently our
1990 * resizing code isn't healthy enough to
1994 set_zorder(TRUE
); /* move to top */
1997 set_zorder(FALSE
); /* move to bottom */
2003 if (esc_nargs
>= 3) {
2004 request_resize(def(esc_args
[2], cfg
.width
),
2005 def(esc_args
[1], cfg
.height
));
2010 set_zoomed(esc_args
[1] ? TRUE
: FALSE
);
2013 ldisc_send(is_iconic() ?
"\033[1t" : "\033[2t",
2017 get_window_pos(&x
, &y
);
2018 len
= sprintf(buf
, "\033[3;%d;%dt", x
, y
);
2019 ldisc_send(buf
, len
, 0);
2022 get_window_pixels(&x
, &y
);
2023 len
= sprintf(buf
, "\033[4;%d;%dt", x
, y
);
2024 ldisc_send(buf
, len
, 0);
2027 len
= sprintf(buf
, "\033[8;%d;%dt",
2029 ldisc_send(buf
, len
, 0);
2033 * Hmmm. Strictly speaking we
2034 * should return `the size of the
2035 * screen in characters', but
2036 * that's not easy: (a) window
2037 * furniture being what it is it's
2038 * hard to compute, and (b) in
2039 * resize-font mode maximising the
2040 * window wouldn't change the
2041 * number of characters. *shrug*. I
2042 * think we'll ignore it for the
2043 * moment and see if anyone
2044 * complains, and then ask them
2045 * what they would like it to do.
2049 p
= get_window_title(TRUE
);
2051 ldisc_send("\033]L", 3, 0);
2052 ldisc_send(p
, len
, 0);
2053 ldisc_send("\033\\", 2, 0);
2056 p
= get_window_title(FALSE
);
2058 ldisc_send("\033]l", 3, 0);
2059 ldisc_send(p
, len
, 0);
2060 ldisc_send("\033\\", 2, 0);
2066 compatibility(SCOANSI
);
2067 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
2070 seen_disp_event
= TRUE
;
2073 compatibility(SCOANSI
);
2074 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
2077 seen_disp_event
= TRUE
;
2079 case ANSI('|', '*'):
2080 /* VT420 sequence DECSNLS
2081 * Set number of lines on screen
2082 * VT420 uses VGA like hardware and can support any size in
2083 * reasonable range (24..49 AIUI) with no default specified.
2085 compatibility(VT420
);
2086 if (esc_nargs
== 1 && esc_args
[0] > 0) {
2087 request_resize(cols
, def(esc_args
[0], cfg
.height
));
2091 case ANSI('|', '$'):
2092 /* VT340/VT420 sequence DECSCPP
2093 * Set number of columns per page
2094 * Docs imply range is only 80 or 132, but I'll allow any.
2096 compatibility(VT340TEXT
);
2097 if (esc_nargs
<= 1) {
2098 request_resize(def(esc_args
[0], cfg
.width
), rows
);
2102 case 'X': /* write N spaces w/o moving cursor */
2103 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2104 compatibility(ANSIMIN
);
2106 int n
= def(esc_args
[0], 1);
2108 unsigned long *p
= cpos
;
2109 if (n
> cols
- curs
.x
)
2113 check_selection(curs
, cursplus
);
2116 seen_disp_event
= TRUE
;
2119 case 'x': /* report terminal characteristics */
2120 compatibility(VT100
);
2123 int i
= def(esc_args
[0], 0);
2124 if (i
== 0 || i
== 1) {
2125 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
2127 ldisc_send(buf
, 20, 0);
2131 case 'Z': /* BackTab for xterm */
2132 compatibility(OTHER
);
2134 int i
= def(esc_args
[0], 1);
2135 pos old_curs
= curs
;
2137 for(;i
>0 && curs
.x
>0; i
--) {
2140 } while (curs
.x
>0 && !tabs
[curs
.x
]);
2143 check_selection(old_curs
, curs
);
2146 case ANSI('L', '='):
2147 compatibility(OTHER
);
2148 use_bce
= (esc_args
[0] <= 0);
2149 erase_char
= ERASE_CHAR
;
2151 erase_char
= (' ' | ATTR_ASCII
|
2153 (ATTR_FGMASK
| ATTR_BGMASK
)));
2155 case ANSI('E', '='):
2156 compatibility(OTHER
);
2157 blink_is_real
= (esc_args
[0] >= 1);
2159 case ANSI('p', '"'):
2160 /* Allow the host to make this emulator a 'perfect' VT102.
2161 * This first appeared in the VT220, but we do need to get
2162 * back to PuTTY mode so I won't check it.
2164 * The arg in 40..42,50 are a PuTTY extension.
2165 * The 2nd arg, 8bit vs 7bit is not checked.
2167 * Setting VT102 mode should also change the Fkeys to
2168 * generate PF* codes as a real VT102 has no Fkeys.
2169 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2172 * Note ESC c will NOT change this!
2175 switch (esc_args
[0]) {
2177 compatibility_level
&= ~TM_VTXXX
;
2178 compatibility_level
|= TM_VT102
;
2181 compatibility_level
&= ~TM_VTXXX
;
2182 compatibility_level
|= TM_VT220
;
2186 if (esc_args
[0] > 60 && esc_args
[0] < 70)
2187 compatibility_level
|= TM_VTXXX
;
2191 compatibility_level
&= TM_VTXXX
;
2194 compatibility_level
= TM_PUTTY
;
2197 compatibility_level
= TM_SCOANSI
;
2201 compatibility_level
= TM_PUTTY
;
2207 /* Change the response to CSI c */
2208 if (esc_args
[0] == 50) {
2211 strcpy(id_string
, "\033[?");
2212 for (i
= 1; i
< esc_nargs
; i
++) {
2214 strcat(id_string
, ";");
2215 sprintf(lbuf
, "%d", esc_args
[i
]);
2216 strcat(id_string
, lbuf
);
2218 strcat(id_string
, "c");
2221 /* Is this a good idea ?
2222 * Well we should do a soft reset at this point ...
2224 if (!has_compat(VT420
) && has_compat(VT100
)) {
2226 request_resize(132, 24);
2228 request_resize(80, 24);
2237 case 'P': /* Linux palette sequence */
2238 termstate
= SEEN_OSC_P
;
2241 case 'R': /* Linux palette reset */
2244 termstate
= TOPLEVEL
;
2246 case 'W': /* word-set */
2247 termstate
= SEEN_OSC_W
;
2260 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2264 * Grotty hack to support xterm and DECterm title
2265 * sequences concurrently.
2267 if (esc_args
[0] == 2) {
2271 /* else fall through */
2273 termstate
= OSC_STRING
;
2279 * This OSC stuff is EVIL. It takes just one character to get into
2280 * sysline mode and it's not initially obvious how to get out.
2281 * So I've added CR and LF as string aborts.
2282 * This shouldn't effect compatibility as I believe embedded
2283 * control characters are supposed to be interpreted (maybe?)
2284 * and they don't display anything useful anyway.
2288 if (c
== '\n' || c
== '\r') {
2289 termstate
= TOPLEVEL
;
2290 } else if (c
== 0234 || c
== '\007') {
2292 * These characters terminate the string; ST and BEL
2293 * terminate the sequence and trigger instant
2294 * processing of it, whereas ESC goes back to SEEN_ESC
2295 * mode unless it is followed by \, in which case it is
2296 * synonymous with ST in the first place.
2299 termstate
= TOPLEVEL
;
2300 } else if (c
== '\033')
2301 termstate
= OSC_MAYBE_ST
;
2302 else if (osc_strlen
< OSC_STR_MAX
)
2303 osc_string
[osc_strlen
++] = c
;
2307 int max
= (osc_strlen
== 0 ?
21 : 16);
2309 if (c
>= '0' && c
<= '9')
2311 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2313 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2316 termstate
= TOPLEVEL
;
2319 osc_string
[osc_strlen
++] = val
;
2320 if (osc_strlen
>= 7) {
2321 palette_set(osc_string
[0],
2322 osc_string
[1] * 16 + osc_string
[2],
2323 osc_string
[3] * 16 + osc_string
[4],
2324 osc_string
[5] * 16 + osc_string
[6]);
2326 termstate
= TOPLEVEL
;
2342 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2345 termstate
= OSC_STRING
;
2350 termstate
= TOPLEVEL
;
2351 seen_disp_event
= TRUE
;
2354 move(curs
.x
, curs
.y
- 1, 1);
2357 move(curs
.x
, curs
.y
+ 1, 1);
2360 move(curs
.x
+ 1, curs
.y
, 1);
2363 move(curs
.x
- 1, curs
.y
, 1);
2366 * From the VT100 Manual
2367 * NOTE: The special graphics characters in the VT100
2368 * are different from those in the VT52
2370 * From VT102 manual:
2371 * 137 _ Blank - Same
2372 * 140 ` Reserved - Humm.
2373 * 141 a Solid rectangle - Similar
2374 * 142 b 1/ - Top half of fraction for the
2375 * 143 c 3/ - subscript numbers below.
2378 * 146 f Degrees - Same
2379 * 147 g Plus or minus - Same
2381 * 151 i Ellipsis (dots)
2384 * 154 l Bar at scan 0
2385 * 155 m Bar at scan 1
2386 * 156 n Bar at scan 2
2387 * 157 o Bar at scan 3 - Similar
2388 * 160 p Bar at scan 4 - Similar
2389 * 161 q Bar at scan 5 - Similar
2390 * 162 r Bar at scan 6 - Same
2391 * 163 s Bar at scan 7 - Similar
2406 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2409 cset_attr
[cset
= 0] = ATTR_ASCII
;
2416 scroll(0, rows
- 1, -1, TRUE
);
2417 else if (curs
.y
> 0)
2423 erase_lots(FALSE
, FALSE
, TRUE
);
2427 erase_lots(TRUE
, FALSE
, TRUE
);
2431 /* XXX Print cursor line */
2434 /* XXX Start controller mode */
2437 /* XXX Stop controller mode */
2441 termstate
= VT52_Y1
;
2444 ldisc_send("\033/Z", 3, 0);
2447 app_keypad_keys
= TRUE
;
2450 app_keypad_keys
= FALSE
;
2453 /* XXX This should switch to VT100 mode not current or default
2454 * VT mode. But this will only have effect in a VT220+
2458 blink_is_real
= cfg
.blinktext
;
2462 /* XXX Enter auto print mode */
2465 /* XXX Exit auto print mode */
2468 /* XXX Print screen */
2474 /* compatibility(ATARI) */
2476 erase_lots(FALSE
, FALSE
, TRUE
);
2480 /* compatibility(ATARI) */
2481 if (curs
.y
<= marg_b
)
2482 scroll(curs
.y
, marg_b
, -1, FALSE
);
2485 /* compatibility(ATARI) */
2486 if (curs
.y
<= marg_b
)
2487 scroll(curs
.y
, marg_b
, 1, TRUE
);
2490 /* compatibility(ATARI) */
2491 termstate
= VT52_FG
;
2494 /* compatibility(ATARI) */
2495 termstate
= VT52_BG
;
2498 /* compatibility(ATARI) */
2499 erase_lots(FALSE
, TRUE
, FALSE
);
2503 /* compatibility(ATARI) */
2507 /* compatibility(ATARI) */
2510 /* case 'j': Save cursor position - broken on ST */
2511 /* case 'k': Restore cursor position */
2513 /* compatibility(ATARI) */
2514 erase_lots(TRUE
, TRUE
, TRUE
);
2520 /* compatibility(ATARI) */
2521 erase_lots(TRUE
, TRUE
, FALSE
);
2524 /* compatibility(ATARI) */
2525 curr_attr
|= ATTR_REVERSE
;
2528 /* compatibility(ATARI) */
2529 curr_attr
&= ~ATTR_REVERSE
;
2531 case 'v': /* wrap Autowrap on - Wyse style */
2532 /* compatibility(ATARI) */
2535 case 'w': /* Autowrap off */
2536 /* compatibility(ATARI) */
2541 /* compatibility(OTHER) */
2543 curr_attr
= ATTR_DEFAULT
;
2545 erase_char
= (' ' | ATTR_ASCII
|
2547 (ATTR_FGMASK
| ATTR_BGMASK
)));
2550 /* compatibility(VI50) */
2551 curr_attr
|= ATTR_UNDER
;
2554 /* compatibility(VI50) */
2555 curr_attr
&= ~ATTR_UNDER
;
2558 /* compatibility(VI50) */
2560 curr_attr
|= ATTR_BOLD
;
2563 /* compatibility(VI50) */
2565 curr_attr
&= ~ATTR_BOLD
;
2571 termstate
= VT52_Y2
;
2572 move(curs
.x
, c
- ' ', 0);
2575 termstate
= TOPLEVEL
;
2576 move(c
- ' ', curs
.y
, 0);
2581 termstate
= TOPLEVEL
;
2582 curr_attr
&= ~ATTR_FGMASK
;
2583 curr_attr
&= ~ATTR_BOLD
;
2584 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2585 if ((c
& 0x8) || vt52_bold
)
2586 curr_attr
|= ATTR_BOLD
;
2589 erase_char
= (' ' | ATTR_ASCII
|
2590 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2593 termstate
= TOPLEVEL
;
2594 curr_attr
&= ~ATTR_BGMASK
;
2595 curr_attr
&= ~ATTR_BLINK
;
2596 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2598 /* Note: bold background */
2600 curr_attr
|= ATTR_BLINK
;
2603 erase_char
= (' ' | ATTR_ASCII
|
2604 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2607 default: break; /* placate gcc warning about enum use */
2609 if (selstate
!= NO_SELECTION
) {
2610 pos cursplus
= curs
;
2612 check_selection(curs
, cursplus
);
2619 * Compare two lines to determine whether they are sufficiently
2620 * alike to scroll-optimise one to the other. Return the degree of
2623 static int linecmp(unsigned long *a
, unsigned long *b
)
2627 for (i
= n
= 0; i
< cols
; i
++)
2628 n
+= (*a
++ == *b
++);
2634 * Given a context, update the window. Out of paranoia, we don't
2635 * allow WM_PAINT responses to do scrolling optimisations.
2637 static void do_paint(Context ctx
, int may_optimise
)
2639 int i
, j
, our_curs_y
;
2640 unsigned long rv
, cursor
;
2643 long cursor_background
= ERASE_CHAR
;
2644 unsigned long ticks
;
2647 * Check the visual bell state.
2650 ticks
= GetTickCount();
2651 if (ticks
- vbell_startpoint
>= VBELL_TIMEOUT
)
2655 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2658 * screen array, disptop, scrtop,
2660 * cfg.blinkpc, blink_is_real, tblinker,
2661 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2664 /* Has the cursor position or type changed ? */
2667 if (blinker
|| !cfg
.blink_cur
)
2668 cursor
= TATTR_ACTCURS
;
2672 cursor
= TATTR_PASCURS
;
2674 cursor
|= TATTR_RIGHTCURS
;
2677 our_curs_y
= curs
.y
- disptop
;
2679 if (dispcurs
&& (curstype
!= cursor
||
2681 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2682 if (dispcurs
> disptext
&&
2683 (*dispcurs
& (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2684 dispcurs
[-1] |= ATTR_INVALID
;
2685 if ( (dispcurs
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2686 dispcurs
[1] |= ATTR_INVALID
;
2687 *dispcurs
|= ATTR_INVALID
;
2692 /* The normal screen data */
2693 for (i
= 0; i
< rows
; i
++) {
2694 unsigned long *ldata
;
2696 int idx
, dirty_line
, dirty_run
, selected
;
2697 unsigned long attr
= 0;
2698 int updated_line
= 0;
2701 int last_run_dirty
= 0;
2703 scrpos
.y
= i
+ disptop
;
2704 ldata
= lineptr(scrpos
.y
);
2705 lattr
= (ldata
[cols
] & LATTR_MODE
);
2707 idx
= i
* (cols
+ 1);
2708 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2709 disptext
[idx
+ cols
] = ldata
[cols
];
2711 for (j
= 0; j
< cols
; j
++, idx
++) {
2712 unsigned long tattr
, tchar
;
2713 unsigned long *d
= ldata
+ j
;
2717 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2718 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2719 switch (tchar
& CSET_MASK
) {
2721 tchar
= unitab_line
[tchar
& 0xFF];
2724 tchar
= unitab_xterm
[tchar
& 0xFF];
2727 tchar
= unitab_scoacs
[tchar
&0xFF];
2730 tattr
|= (tchar
& CSET_MASK
);
2732 if ((d
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2735 /* Video reversing things */
2736 if (seltype
== LEXICOGRAPHIC
)
2737 selected
= posle(selstart
, scrpos
) && poslt(scrpos
, selend
);
2739 selected
= posPle(selstart
, scrpos
) && posPlt(scrpos
, selend
);
2741 ^ (selected ? ATTR_REVERSE
: 0));
2743 /* 'Real' blinking ? */
2744 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2745 if (has_focus
&& tblinker
) {
2747 tattr
&= ~CSET_MASK
;
2750 tattr
&= ~ATTR_BLINK
;
2754 * Check the font we'll _probably_ be using to see if
2755 * the character is wide when we don't want it to be.
2757 if ((tchar
| tattr
) != (disptext
[idx
]& ~ATTR_NARROW
)) {
2758 if ((tattr
& ATTR_WIDE
) == 0 &&
2759 CharWidth(ctx
, (tchar
| tattr
) & 0xFFFF) == 2)
2760 tattr
|= ATTR_NARROW
;
2761 } else if (disptext
[idx
]&ATTR_NARROW
)
2762 tattr
|= ATTR_NARROW
;
2764 /* Cursor here ? Save the 'background' */
2765 if (i
== our_curs_y
&& j
== curs
.x
) {
2766 cursor_background
= tattr
| tchar
;
2767 dispcurs
= disptext
+ idx
;
2770 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2773 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2775 /* Special hack for VT100 Linedraw glyphs */
2776 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2777 && tchar
<= 0xBD) break_run
= TRUE
;
2779 if (!dbcs_screenfont
&& !dirty_line
) {
2780 if ((tchar
| tattr
) == disptext
[idx
])
2782 else if (!dirty_run
&& ccount
== 1)
2787 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2788 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2794 if (dbcs_screenfont
)
2795 last_run_dirty
= dirty_run
;
2796 dirty_run
= dirty_line
;
2799 if ((tchar
| tattr
) != disptext
[idx
])
2801 ch
[ccount
++] = (char) tchar
;
2802 disptext
[idx
] = tchar
| tattr
;
2804 /* If it's a wide char step along to the next one. */
2805 if (tattr
& ATTR_WIDE
) {
2809 /* Cursor is here ? Ouch! */
2810 if (i
== our_curs_y
&& j
== curs
.x
) {
2811 cursor_background
= *d
;
2812 dispcurs
= disptext
+ idx
;
2814 if (disptext
[idx
] != *d
)
2820 if (dirty_run
&& ccount
> 0) {
2821 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2825 /* Cursor on this line ? (and changed) */
2826 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2827 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2828 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2829 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2836 * Flick the switch that says if blinking things should be shown or hidden.
2839 void term_blink(int flg
)
2841 static long last_blink
= 0;
2842 static long last_tblink
= 0;
2843 long now
, blink_diff
;
2845 now
= GetTickCount();
2846 blink_diff
= now
- last_tblink
;
2848 /* Make sure the text blinks no more than 2Hz */
2849 if (blink_diff
< 0 || blink_diff
> 450) {
2851 tblinker
= !tblinker
;
2860 blink_diff
= now
- last_blink
;
2862 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2863 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2871 * Invalidate the whole screen so it will be repainted in full.
2873 void term_invalidate(void)
2877 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2878 disptext
[i
] = ATTR_INVALID
;
2882 * Paint the window in response to a WM_PAINT message.
2884 void term_paint(Context ctx
, int left
, int top
, int right
, int bottom
)
2887 if (left
< 0) left
= 0;
2888 if (top
< 0) top
= 0;
2889 if (right
>= cols
) right
= cols
-1;
2890 if (bottom
>= rows
) bottom
= rows
-1;
2892 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2893 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2894 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2895 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2897 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2898 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2901 /* This should happen soon enough, also for some reason it sometimes
2902 * fails to actually do anything when re-sizing ... painting the wrong
2906 do_paint (ctx
, FALSE
);
2910 * Attempt to scroll the scrollback. The second parameter gives the
2911 * position we want to scroll to; the first is +1 to denote that
2912 * this position is relative to the beginning of the scrollback, -1
2913 * to denote it is relative to the end, and 0 to denote that it is
2914 * relative to the current position.
2916 void term_scroll(int rel
, int where
)
2918 int sbtop
= -count234(scrollback
);
2920 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2921 if (disptop
< sbtop
)
2929 static void clipme(pos top
, pos bottom
, int rect
)
2932 wchar_t *wbptr
; /* where next char goes within workbuf */
2934 int wblen
= 0; /* workbuf len */
2935 int buflen
; /* amount of memory allocated to workbuf */
2937 buflen
= 5120; /* Default size */
2938 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2939 wbptr
= workbuf
; /* start filling here */
2940 old_top_x
= top
.x
; /* needed for rect==1 */
2942 while (poslt(top
, bottom
)) {
2944 unsigned long *ldata
= lineptr(top
.y
);
2948 * nlpos will point at the maximum position on this line we
2949 * should copy up to. So we start it at the end of the
2956 * ... move it backwards if there's unused space at the end
2957 * of the line (and also set `nl' if this is the case,
2958 * because in normal selection mode this means we need a
2959 * newline at the end)...
2961 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
2962 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
2963 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
2964 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
2965 && poslt(top
, nlpos
))
2967 if (poslt(nlpos
, bottom
))
2972 * ... and then clip it to the terminal x coordinate if
2973 * we're doing rectangular selection. (In this case we
2974 * still did the above, so that copying e.g. the right-hand
2975 * column from a table doesn't fill with spaces on the
2979 if (nlpos
.x
> bottom
.x
)
2981 nl
= (top
.y
< bottom
.y
);
2984 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
2987 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
2989 wchar_t cbuf
[16], *p
;
2990 int uc
= (ldata
[top
.x
] & 0xFFFF);
2993 if (uc
== UCSWIDE
) {
2998 switch (uc
& CSET_MASK
) {
3001 uc
= unitab_xterm
[uc
& 0xFF];
3005 uc
= unitab_line
[uc
& 0xFF];
3008 uc
= unitab_scoacs
[uc
&0xFF];
3011 switch (uc
& CSET_MASK
) {
3013 uc
= unitab_font
[uc
& 0xFF];
3016 uc
= unitab_oemcp
[uc
& 0xFF];
3020 set
= (uc
& CSET_MASK
);
3021 c
= (uc
& CHAR_MASK
);
3025 if (DIRECT_FONT(uc
)) {
3026 if (c
>= ' ' && c
!= 0x7F) {
3027 unsigned char buf
[4];
3030 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
3032 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
3033 rv
= MultiByteToWideChar(font_codepage
,
3034 0, buf
, 2, wbuf
, 4);
3038 rv
= MultiByteToWideChar(font_codepage
,
3039 0, buf
, 1, wbuf
, 4);
3043 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
3050 for (p
= cbuf
; *p
; p
++) {
3051 /* Enough overhead for trailing NL and nul */
3052 if (wblen
>= buflen
- 16) {
3055 sizeof(wchar_t) * (buflen
+= 100));
3056 wbptr
= workbuf
+ wblen
;
3065 for (i
= 0; i
< sel_nl_sz
; i
++) {
3067 *wbptr
++ = sel_nl
[i
];
3071 top
.x
= rect ? old_top_x
: 0;
3075 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
3076 if (buflen
> 0) /* indicates we allocated this buffer */
3080 void term_copyall(void)
3083 top
.y
= -count234(scrollback
);
3085 clipme(top
, curs
, 0);
3089 * The wordness array is mainly for deciding the disposition of the US-ASCII
3092 static int wordtype(int uc
)
3095 int start
, end
, ctype
;
3096 } *wptr
, ucs_words
[] = {
3102 0x037e, 0x037e, 1}, /* Greek question mark */
3104 0x0387, 0x0387, 1}, /* Greek ano teleia */
3106 0x055a, 0x055f, 1}, /* Armenian punctuation */
3108 0x0589, 0x0589, 1}, /* Armenian full stop */
3110 0x0700, 0x070d, 1}, /* Syriac punctuation */
3112 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3114 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3116 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3118 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3120 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3122 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3124 0x2000, 0x200a, 0}, /* Various spaces */
3126 0x2070, 0x207f, 2}, /* superscript */
3128 0x2080, 0x208f, 2}, /* subscript */
3130 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3132 0x3000, 0x3000, 0}, /* ideographic space */
3134 0x3001, 0x3020, 1}, /* ideographic punctuation */
3136 0x303f, 0x309f, 3}, /* Hiragana */
3138 0x30a0, 0x30ff, 3}, /* Katakana */
3140 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3142 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3144 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3146 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3148 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3150 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3152 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3154 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3156 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3161 uc
&= (CSET_MASK
| CHAR_MASK
);
3163 switch (uc
& CSET_MASK
) {
3165 uc
= unitab_xterm
[uc
& 0xFF];
3168 uc
= unitab_line
[uc
& 0xFF];
3171 uc
= unitab_scoacs
[uc
&0xFF];
3174 switch (uc
& CSET_MASK
) {
3176 uc
= unitab_font
[uc
& 0xFF];
3179 uc
= unitab_oemcp
[uc
& 0xFF];
3183 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3184 * fail as there's such a thing as a double width space. :-(
3186 if (dbcs_screenfont
&& font_codepage
== line_codepage
)
3190 return wordness
[uc
];
3192 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
3193 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
3201 * Spread the selection outwards according to the selection mode.
3203 static pos
sel_spread_half(pos p
, int dir
)
3205 unsigned long *ldata
;
3207 int topy
= -count234(scrollback
);
3209 ldata
= lineptr(p
.y
);
3214 * In this mode, every character is a separate unit, except
3215 * for runs of spaces at the end of a non-wrapping line.
3217 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
3218 unsigned long *q
= ldata
+ cols
;
3219 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
3221 if (q
== ldata
+ cols
)
3223 if (p
.x
>= q
- ldata
)
3224 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
3229 * In this mode, the units are maximal runs of characters
3230 * whose `wordness' has the same value.
3232 wvalue
= wordtype(ldata
[p
.x
]);
3236 if (wordtype(ldata
[p
.x
+ 1]) == wvalue
)
3241 if (ldata
[cols
] & LATTR_WRAPPED
) {
3242 unsigned long *ldata2
;
3243 ldata2
= lineptr(p
.y
+1);
3244 if (wordtype(ldata2
[0]) == wvalue
) {
3257 if (wordtype(ldata
[p
.x
- 1]) == wvalue
)
3262 unsigned long *ldata2
;
3265 ldata2
= lineptr(p
.y
-1);
3266 if ((ldata2
[cols
] & LATTR_WRAPPED
) &&
3267 wordtype(ldata2
[cols
-1]) == wvalue
) {
3279 * In this mode, every line is a unit.
3281 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3287 static void sel_spread(void)
3289 if (seltype
== LEXICOGRAPHIC
) {
3290 selstart
= sel_spread_half(selstart
, -1);
3292 selend
= sel_spread_half(selend
, +1);
3297 void term_do_paste(void)
3302 get_clip(&data
, &len
);
3307 sfree(paste_buffer
);
3308 paste_pos
= paste_hold
= paste_len
= 0;
3309 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3312 while (p
< data
+ len
) {
3313 while (p
< data
+ len
&&
3314 !(p
<= data
+ len
- sel_nl_sz
&&
3315 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3320 for (i
= 0; i
< p
- q
; i
++) {
3321 paste_buffer
[paste_len
++] = q
[i
];
3325 if (p
<= data
+ len
- sel_nl_sz
&&
3326 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3327 paste_buffer
[paste_len
++] = '\r';
3333 /* Assume a small paste will be OK in one go. */
3334 if (paste_len
< 256) {
3335 luni_send(paste_buffer
, paste_len
, 0);
3337 sfree(paste_buffer
);
3339 paste_pos
= paste_hold
= paste_len
= 0;
3342 get_clip(NULL
, NULL
);
3345 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3346 int shift
, int ctrl
, int alt
)
3349 unsigned long *ldata
;
3350 int raw_mouse
= xterm_mouse
&& !(cfg
.mouse_override
&& shift
);
3351 int default_seltype
;
3355 if (a
== MA_DRAG
&& !raw_mouse
)
3360 if (a
== MA_DRAG
&& !raw_mouse
)
3373 selpoint
.y
= y
+ disptop
;
3375 ldata
= lineptr(selpoint
.y
);
3376 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3380 int encstate
= 0, r
, c
;
3382 static int is_down
= 0;
3386 encstate
= 0x20; /* left button down */
3397 case MBT_WHEEL_DOWN
:
3400 default: break; /* placate gcc warning about enum use */
3404 if (xterm_mouse
== 1)
3417 default: break; /* placate gcc warning about enum use */
3426 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3427 ldisc_send(abuf
, 6, 0);
3431 b
= translate_button(b
);
3434 * Set the selection type (rectangular or normal) at the start
3435 * of a selection attempt, from the state of Alt.
3437 if (!alt
^ !cfg
.rect_select
)
3438 default_seltype
= RECTANGULAR
;
3440 default_seltype
= LEXICOGRAPHIC
;
3442 if (selstate
== NO_SELECTION
) {
3443 seltype
= default_seltype
;
3446 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3448 selstate
= ABOUT_TO
;
3449 seltype
= default_seltype
;
3450 selanchor
= selpoint
;
3452 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3454 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3455 selstate
= DRAGGING
;
3456 selstart
= selanchor
= selpoint
;
3460 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3461 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3462 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3464 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3465 if (seltype
== LEXICOGRAPHIC
) {
3467 * For normal selection, we extend by moving
3468 * whichever end of the current selection is closer
3471 if (posdiff(selpoint
, selstart
) <
3472 posdiff(selend
, selstart
) / 2) {
3476 selanchor
= selstart
;
3480 * For rectangular selection, we have a choice of
3481 * _four_ places to put selanchor and selpoint: the
3482 * four corners of the selection.
3484 if (2*selpoint
.x
< selstart
.x
+ selend
.x
)
3485 selanchor
.x
= selend
.x
-1;
3487 selanchor
.x
= selstart
.x
;
3489 if (2*selpoint
.y
< selstart
.y
+ selend
.y
)
3490 selanchor
.y
= selend
.y
;
3492 selanchor
.y
= selstart
.y
;
3494 selstate
= DRAGGING
;
3496 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3497 selanchor
= selpoint
;
3498 selstate
= DRAGGING
;
3499 if (seltype
== LEXICOGRAPHIC
) {
3501 * For normal selection, we set (selstart,selend) to
3502 * (selpoint,selanchor) in some order.
3504 if (poslt(selpoint
, selanchor
)) {
3505 selstart
= selpoint
;
3509 selstart
= selanchor
;
3515 * For rectangular selection, we may need to
3516 * interchange x and y coordinates (if the user has
3517 * dragged in the -x and +y directions, or vice versa).
3519 selstart
.x
= min(selanchor
.x
, selpoint
.x
);
3520 selend
.x
= 1+max(selanchor
.x
, selpoint
.x
);
3521 selstart
.y
= min(selanchor
.y
, selpoint
.y
);
3522 selend
.y
= max(selanchor
.y
, selpoint
.y
);
3525 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3526 if (selstate
== DRAGGING
) {
3528 * We've completed a selection. We now transfer the
3529 * data to the clipboard.
3531 clipme(selstart
, selend
, (seltype
== RECTANGULAR
));
3532 selstate
= SELECTED
;
3534 selstate
= NO_SELECTION
;
3535 } else if (b
== MBT_PASTE
3536 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3547 sfree(paste_buffer
);
3554 static long last_paste
= 0;
3555 long now
, paste_diff
;
3560 /* Don't wait forever to paste */
3562 now
= GetTickCount();
3563 paste_diff
= now
- last_paste
;
3564 if (paste_diff
>= 0 && paste_diff
< 450)
3569 while (paste_pos
< paste_len
) {
3571 while (n
+ paste_pos
< paste_len
) {
3572 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3575 luni_send(paste_buffer
+ paste_pos
, n
, 0);
3578 if (paste_pos
< paste_len
) {
3583 sfree(paste_buffer
);
3588 static void deselect(void)
3590 selstate
= NO_SELECTION
;
3591 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3594 void term_deselect(void)
3600 int term_ldisc(int option
)
3602 if (option
== LD_ECHO
)
3603 return term_echoing
;
3604 if (option
== LD_EDIT
)
3605 return term_editing
;
3610 * from_backend(), to get data from the backend for the terminal.
3612 int from_backend(int is_stderr
, char *data
, int len
)
3616 bufchain_add(&inbuf
, data
, len
);
3619 * term_out() always completely empties inbuf. Therefore,
3620 * there's no reason at all to return anything other than zero
3621 * from this function, because there _can't_ be a question of
3622 * the remote side needing to wait until term_out() has cleared
3625 * This is a slightly suboptimal way to deal with SSH2 - in
3626 * principle, the window mechanism would allow us to continue
3627 * to accept data on forwarded ports and X connections even
3628 * while the terminal processing was going slowly - but we
3629 * can't do the 100% right thing without moving the terminal
3630 * processing into a separate thread, and that might hurt
3631 * portability. So we manage stdout buffering the old SSH1 way:
3632 * if the terminal processing goes slowly, the whole SSH
3633 * connection stops accepting data until it's ready.
3635 * In practice, I can't imagine this causing serious trouble.