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 * When the user reconfigures us, we need to check the forbidden-
355 * alternate-screen config option.
357 void term_reconfig(void)
359 if (cfg
.no_alt_screen
)
361 if (cfg
.no_remote_charset
) {
362 cset_attr
[0] = cset_attr
[1] = ATTR_ASCII
;
363 sco_acs
= alt_sco_acs
= 0;
369 * Clear the scrollback.
371 void term_clrsb(void)
375 while ((line
= delpos234(scrollback
, 0)) != NULL
) {
382 * Initialise the terminal.
386 screen
= alt_screen
= scrollback
= NULL
;
388 disptext
= dispcurs
= NULL
;
393 beephead
= beeptail
= NULL
;
396 beep_overloaded
= FALSE
;
400 * Set up the terminal for a given size.
402 void term_size(int newrows
, int newcols
, int newsavelines
)
405 unsigned long *newdisp
, *line
;
408 int save_alt_which
= alt_which
;
410 if (newrows
== rows
&& newcols
== cols
&& newsavelines
== savelines
)
411 return; /* nothing to do */
417 alt_b
= marg_b
= newrows
- 1;
420 scrollback
= newtree234(NULL
);
421 screen
= newtree234(NULL
);
426 * Resize the screen and scrollback. We only need to shift
427 * lines around within our data structures, because lineptr()
428 * will take care of resizing each individual line if
431 * - If the new screen and the old screen differ in length, we
432 * must shunt some lines in from the scrollback or out to
435 * - If doing that fails to provide us with enough material to
436 * fill the new screen (i.e. the number of rows needed in
437 * the new screen exceeds the total number in the previous
438 * screen+scrollback), we must invent some blank lines to
441 * - Then, if the new scrollback length is less than the
442 * amount of scrollback we actually have, we must throw some
445 sblen
= count234(scrollback
);
446 /* Do this loop to expand the screen if newrows > rows */
447 for (i
= rows
; i
< newrows
; i
++) {
449 line
= delpos234(scrollback
, --sblen
);
451 line
= smalloc(TSIZE
* (newcols
+ 2));
453 for (j
= 0; j
<= newcols
; j
++)
454 line
[j
+ 1] = ERASE_CHAR
;
456 addpos234(screen
, line
, 0);
458 /* Do this loop to shrink the screen if newrows < rows */
459 for (i
= newrows
; i
< rows
; i
++) {
460 line
= delpos234(screen
, 0);
461 addpos234(scrollback
, line
, sblen
++);
463 assert(count234(screen
) == newrows
);
464 while (sblen
> newsavelines
) {
465 line
= delpos234(scrollback
, 0);
469 assert(count234(scrollback
) <= newsavelines
);
472 newdisp
= smalloc(newrows
* (newcols
+ 1) * TSIZE
);
473 for (i
= 0; i
< newrows
* (newcols
+ 1); i
++)
474 newdisp
[i
] = ATTR_INVALID
;
479 newalt
= newtree234(NULL
);
480 for (i
= 0; i
< newrows
; i
++) {
481 line
= smalloc(TSIZE
* (newcols
+ 2));
483 for (j
= 0; j
<= newcols
; j
++)
484 line
[j
+ 1] = erase_char
;
485 addpos234(newalt
, line
, i
);
488 while (NULL
!= (line
= delpos234(alt_screen
, 0)))
490 freetree234(alt_screen
);
494 tabs
= srealloc(tabs
, newcols
* sizeof(*tabs
));
497 for (i
= (cols
> 0 ? cols
: 0); i
< newcols
; i
++)
498 tabs
[i
] = (i
% 8 == 0 ? TRUE
: FALSE
);
502 curs
.y
+= newrows
- rows
;
505 if (curs
.y
>= newrows
)
506 curs
.y
= newrows
- 1;
507 if (curs
.x
>= newcols
)
508 curs
.x
= newcols
- 1;
510 wrapnext
= alt_wnext
= FALSE
;
514 savelines
= newsavelines
;
517 swap_screen(save_alt_which
);
527 static void swap_screen(int which
)
532 if (which
== alt_which
)
559 wrapnext
= alt_wnext
;
571 sco_acs
= alt_sco_acs
;
578 * Update the scroll bar.
580 static void update_sbar(void)
584 nscroll
= count234(scrollback
);
586 set_sbar(nscroll
+ rows
, nscroll
+ disptop
, rows
);
590 * Check whether the region bounded by the two pointers intersects
591 * the scroll region, and de-select the on-screen selection if so.
593 static void check_selection(pos from
, pos to
)
595 if (poslt(from
, selend
) && poslt(selstart
, to
))
600 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
601 * for backward.) `sb' is TRUE if the scrolling is permitted to
602 * affect the scrollback buffer.
604 * NB this function invalidates all pointers into lines of the
605 * screen data structures. In particular, you MUST call fix_cpos
606 * after calling scroll() and before doing anything else that
607 * uses the cpos shortcut pointer.
609 static void scroll(int topline
, int botline
, int lines
, int sb
)
611 unsigned long *line
, *line2
;
614 if (topline
!= 0 || alt_which
!= 0)
619 line
= delpos234(screen
, botline
);
620 line
= resizeline(line
, cols
);
621 for (i
= 0; i
< cols
; i
++)
622 line
[i
+ 1] = erase_char
;
624 addpos234(screen
, line
, topline
);
626 if (selstart
.y
>= topline
&& selstart
.y
<= botline
) {
628 if (selstart
.y
> botline
) {
629 selstart
.y
= botline
;
633 if (selend
.y
>= topline
&& selend
.y
<= botline
) {
635 if (selend
.y
> botline
) {
645 line
= delpos234(screen
, topline
);
646 if (sb
&& savelines
> 0) {
647 int sblen
= count234(scrollback
);
649 * We must add this line to the scrollback. We'll
650 * remove a line from the top of the scrollback to
651 * replace it, or allocate a new one if the
652 * scrollback isn't full.
654 if (sblen
== savelines
) {
655 sblen
--, line2
= delpos234(scrollback
, 0);
657 line2
= smalloc(TSIZE
* (cols
+ 2));
660 addpos234(scrollback
, line
, sblen
);
664 * If the user is currently looking at part of the
665 * scrollback, and they haven't enabled any options
666 * that are going to reset the scrollback as a
667 * result of this movement, then the chances are
668 * they'd like to keep looking at the same line. So
669 * we move their viewpoint at the same rate as the
670 * scroll, at least until their viewpoint hits the
671 * top end of the scrollback buffer, at which point
672 * we don't have the choice any more.
674 * Thanks to Jan Holmen Holsten for the idea and
675 * initial implementation.
677 if (disptop
> -savelines
&& disptop
< 0)
680 line
= resizeline(line
, cols
);
681 for (i
= 0; i
< cols
; i
++)
682 line
[i
+ 1] = erase_char
;
684 addpos234(screen
, line
, botline
);
687 * If the selection endpoints move into the scrollback,
688 * we keep them moving until they hit the top. However,
689 * of course, if the line _hasn't_ moved into the
690 * scrollback then we don't do this, and cut them off
691 * at the top of the scroll region.
693 * This applies to selstart and selend (for an existing
694 * selection), and also selanchor (for one being
695 * selected as we speak).
697 seltop
= sb ?
-savelines
: topline
;
699 if (selstart
.y
>= seltop
&& selstart
.y
<= botline
) {
701 if (selstart
.y
< seltop
) {
706 if (selend
.y
>= seltop
&& selend
.y
<= botline
) {
708 if (selend
.y
< seltop
) {
713 if (selanchor
.y
>= seltop
&& selanchor
.y
<= botline
) {
715 if (selanchor
.y
< seltop
) {
716 selanchor
.y
= seltop
;
727 * Move the cursor to a given position, clipping at boundaries. We
728 * may or may not want to clip at the scroll margin: marg_clip is 0
729 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
730 * even _being_ outside the margins.
732 static void move(int x
, int y
, int marg_clip
)
739 if ((curs
.y
>= marg_t
|| marg_clip
== 2) && y
< marg_t
)
741 if ((curs
.y
<= marg_b
|| marg_clip
== 2) && y
> marg_b
)
755 * Save or restore the cursor and SGR mode.
757 static void save_cursor(int save
)
761 save_attr
= curr_attr
;
764 save_wnext
= wrapnext
;
765 save_csattr
= cset_attr
[cset
];
766 save_sco_acs
= sco_acs
;
769 /* Make sure the window hasn't shrunk since the save */
775 curr_attr
= save_attr
;
778 wrapnext
= save_wnext
;
780 * wrapnext might reset to False if the x position is no
781 * longer at the rightmost edge.
783 if (wrapnext
&& curs
.x
< cols
-1)
785 cset_attr
[cset
] = save_csattr
;
786 sco_acs
= save_sco_acs
;
789 erase_char
= (' ' | ATTR_ASCII
|
790 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
795 * Erase a large portion of the screen: the whole screen, or the
796 * whole line, or parts thereof.
798 static void erase_lots(int line_only
, int from_begin
, int to_end
)
802 unsigned long *ldata
;
824 check_selection(start
, end
);
826 /* Clear screen also forces a full window redraw, just in case. */
827 if (start
.y
== 0 && start
.x
== 0 && end
.y
== rows
)
830 ldata
= lineptr(start
.y
);
831 while (poslt(start
, end
)) {
832 if (start
.x
== cols
&& !erase_lattr
)
833 ldata
[start
.x
] &= ~LATTR_WRAPPED
;
835 ldata
[start
.x
] = erase_char
;
836 if (incpos(start
) && start
.y
< rows
)
837 ldata
= lineptr(start
.y
);
842 * Insert or delete characters within the current line. n is +ve if
843 * insertion is desired, and -ve for deletion.
845 static void insch(int n
)
847 int dir
= (n
< 0 ?
-1 : +1);
850 unsigned long *ldata
;
852 n
= (n
< 0 ?
-n
: n
);
853 if (n
> cols
- curs
.x
)
855 m
= cols
- curs
.x
- n
;
857 cursplus
.x
= curs
.x
+ n
;
858 check_selection(curs
, cursplus
);
859 ldata
= lineptr(curs
.y
);
861 memmove(ldata
+ curs
.x
, ldata
+ curs
.x
+ n
, m
* TSIZE
);
863 ldata
[curs
.x
+ m
++] = erase_char
;
865 memmove(ldata
+ curs
.x
+ n
, ldata
+ curs
.x
, m
* TSIZE
);
867 ldata
[curs
.x
+ n
] = erase_char
;
872 * Toggle terminal mode `mode' to state `state'. (`query' indicates
873 * whether the mode is a DEC private one or a normal one.)
875 static void toggle_mode(int mode
, int query
, int state
)
881 case 1: /* application cursor keys */
882 app_cursor_keys
= state
;
884 case 2: /* VT52 mode */
887 blink_is_real
= FALSE
;
890 blink_is_real
= cfg
.blinktext
;
893 case 3: /* 80/132 columns */
895 if (!cfg
.no_remote_resize
)
896 request_resize(state ?
132 : 80, rows
);
899 case 5: /* reverse video */
901 * Toggle reverse video. If we receive an OFF within the
902 * visual bell timeout period after an ON, we trigger an
903 * effective visual bell, so that ESC[?5hESC[?5l will
904 * always be an actually _visible_ visual bell.
906 ticks
= GetTickCount();
907 /* turn off a previous vbell to avoid inconsistencies */
908 if (ticks
- vbell_startpoint
>= VBELL_TIMEOUT
)
910 if (rvideo
&& !state
&& /* we're turning it off... */
911 (ticks
- rvbell_startpoint
) < VBELL_TIMEOUT
) { /* ...soon */
912 /* If there's no vbell timeout already, or this one lasts
913 * longer, replace vbell_timeout with ours. */
915 (rvbell_startpoint
- vbell_startpoint
< VBELL_TIMEOUT
))
916 vbell_startpoint
= rvbell_startpoint
;
917 in_vbell
= TRUE
; /* we may clear rvideo but we set in_vbell */
918 } else if (!rvideo
&& state
) {
919 /* This is an ON, so we notice the time and save it. */
920 rvbell_startpoint
= ticks
;
923 seen_disp_event
= TRUE
;
927 case 6: /* DEC origin mode */
930 case 7: /* auto wrap */
933 case 8: /* auto key repeat */
936 case 10: /* set local edit mode */
937 term_editing
= state
;
938 ldisc_send(NULL
, 0, 0); /* cause ldisc to notice changes */
940 case 25: /* enable/disable cursor */
941 compatibility2(OTHER
, VT220
);
943 seen_disp_event
= TRUE
;
945 case 47: /* alternate screen */
946 compatibility(OTHER
);
948 swap_screen(cfg
.no_alt_screen ?
0 : state
);
951 case 1000: /* xterm mouse 1 */
952 xterm_mouse
= state ?
1 : 0;
953 set_raw_mouse_mode(state
);
955 case 1002: /* xterm mouse 2 */
956 xterm_mouse
= state ?
2 : 0;
957 set_raw_mouse_mode(state
);
961 case 4: /* set insert mode */
962 compatibility(VT102
);
965 case 12: /* set echo mode */
966 term_echoing
= !state
;
967 ldisc_send(NULL
, 0, 0); /* cause ldisc to notice changes */
969 case 20: /* Return sends ... */
970 cr_lf_return
= state
;
972 case 34: /* Make cursor BIG */
973 compatibility2(OTHER
, VT220
);
979 * Process an OSC sequence: set window title or icon name.
981 static void do_osc(void)
985 wordness
[(unsigned char) osc_string
[osc_strlen
]] = esc_args
[0];
987 osc_string
[osc_strlen
] = '\0';
988 switch (esc_args
[0]) {
991 if (!cfg
.no_remote_wintitle
)
992 set_icon(osc_string
);
993 if (esc_args
[0] == 1)
995 /* fall through: parameter 0 means set both */
998 if (!cfg
.no_remote_wintitle
)
999 set_title(osc_string
);
1006 * Remove everything currently in `inbuf' and stick it up on the
1007 * in-memory display. There's a big state machine in here to
1008 * process escape sequences...
1013 unsigned char localbuf
[256], *chars
;
1018 while (nchars
> 0 || bufchain_size(&inbuf
) > 0) {
1022 bufchain_prefix(&inbuf
, &ret
, &nchars
);
1023 if (nchars
> sizeof(localbuf
))
1024 nchars
= sizeof(localbuf
);
1025 memcpy(localbuf
, ret
, nchars
);
1026 bufchain_consume(&inbuf
, nchars
);
1028 assert(chars
!= NULL
);
1034 * Optionally log the session traffic to a file. Useful for
1035 * debugging and possibly also useful for actual logging.
1037 if (cfg
.logtype
== LGTYP_DEBUG
)
1038 logtraffic((unsigned char) c
, LGTYP_DEBUG
);
1044 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1045 * be able to display 8-bit characters, but I'll let that go 'cause
1049 /* First see about all those translations. */
1050 if (termstate
== TOPLEVEL
) {
1052 switch (utf_state
) {
1055 /* UTF-8 must be stateless so we ignore iso2022. */
1056 if (unitab_ctrl
[c
] != 0xFF)
1058 else c
= ((unsigned char)c
) | ATTR_ASCII
;
1060 } else if ((c
& 0xe0) == 0xc0) {
1061 utf_size
= utf_state
= 1;
1062 utf_char
= (c
& 0x1f);
1063 } else if ((c
& 0xf0) == 0xe0) {
1064 utf_size
= utf_state
= 2;
1065 utf_char
= (c
& 0x0f);
1066 } else if ((c
& 0xf8) == 0xf0) {
1067 utf_size
= utf_state
= 3;
1068 utf_char
= (c
& 0x07);
1069 } else if ((c
& 0xfc) == 0xf8) {
1070 utf_size
= utf_state
= 4;
1071 utf_char
= (c
& 0x03);
1072 } else if ((c
& 0xfe) == 0xfc) {
1073 utf_size
= utf_state
= 5;
1074 utf_char
= (c
& 0x01);
1085 if ((c
& 0xC0) != 0x80) {
1091 utf_char
= (utf_char
<< 6) | (c
& 0x3f);
1097 /* Is somebody trying to be evil! */
1099 (c
< 0x800 && utf_size
>= 2) ||
1100 (c
< 0x10000 && utf_size
>= 3) ||
1101 (c
< 0x200000 && utf_size
>= 4) ||
1102 (c
< 0x4000000 && utf_size
>= 5))
1105 /* Unicode line separator and paragraph separator are CR-LF */
1106 if (c
== 0x2028 || c
== 0x2029)
1109 /* High controls are probably a Baaad idea too. */
1113 /* The UTF-16 surrogates are not nice either. */
1114 /* The standard give the option of decoding these:
1115 * I don't want to! */
1116 if (c
>= 0xD800 && c
< 0xE000)
1119 /* ISO 10646 characters now limited to UTF-16 range. */
1123 /* This is currently a TagPhobic application.. */
1124 if (c
>= 0xE0000 && c
<= 0xE007F)
1127 /* U+FEFF is best seen as a null. */
1130 /* But U+FFFE is an error. */
1131 if (c
== 0xFFFE || c
== 0xFFFF)
1134 /* Oops this is a 16bit implementation */
1139 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1141 (c
!='\033' && c
!='\n' && c
!='\r' && c
!='\b'))
1143 if (sco_acs
== 2) c
^= 0x80;
1146 switch (cset_attr
[cset
]) {
1148 * Linedraw characters are different from 'ESC ( B'
1149 * only for a small range. For ones outside that
1150 * range, make sure we use the same font as well as
1151 * the same encoding.
1154 if (unitab_ctrl
[c
] != 0xFF)
1157 c
= ((unsigned char) c
) | ATTR_LINEDRW
;
1161 /* If UK-ASCII, make the '#' a LineDraw Pound */
1163 c
= '}' | ATTR_LINEDRW
;
1166 /*FALLTHROUGH*/ case ATTR_ASCII
:
1167 if (unitab_ctrl
[c
] != 0xFF)
1170 c
= ((unsigned char) c
) | ATTR_ASCII
;
1173 if (c
>=' ') c
= ((unsigned char)c
) | ATTR_SCOACS
;
1179 /* How about C1 controls ? */
1180 if ((c
& -32) == 0x80 && termstate
< DO_CTRLS
&& !vt52_mode
&&
1181 has_compat(VT220
)) {
1182 termstate
= SEEN_ESC
;
1184 c
= '@' + (c
& 0x1F);
1187 /* Or the GL control. */
1188 if (c
== '\177' && termstate
< DO_CTRLS
&& has_compat(OTHER
)) {
1189 if (curs
.x
&& !wrapnext
)
1193 if (!cfg
.no_dbackspace
) /* destructive bksp might be disabled */
1194 *cpos
= (' ' | curr_attr
| ATTR_ASCII
);
1196 /* Or normal C0 controls. */
1197 if ((c
& -32) == 0 && termstate
< DO_CTRLS
) {
1199 case '\005': /* terminal type query */
1200 /* Strictly speaking this is VT100 but a VT100 defaults to
1201 * no response. Other terminals respond at their option.
1203 * Don't put a CR in the default string as this tends to
1204 * upset some weird software.
1206 * An xterm returns "xterm" (5 characters)
1208 compatibility(ANSIMIN
);
1210 char abuf
[256], *s
, *d
;
1212 for (s
= cfg
.answerback
, d
= abuf
; *s
; s
++) {
1214 if (*s
>= 'a' && *s
<= 'z')
1215 *d
++ = (*s
- ('a' - 1));
1216 else if ((*s
>= '@' && *s
<= '_') ||
1217 *s
== '?' || (*s
& 0x80))
1222 } else if (*s
== '^') {
1227 lpage_send(CP_ACP
, abuf
, d
- abuf
, 0);
1232 struct beeptime
*newbeep
;
1233 unsigned long ticks
;
1235 ticks
= GetTickCount();
1237 if (!beep_overloaded
) {
1238 newbeep
= smalloc(sizeof(struct beeptime
));
1239 newbeep
->ticks
= ticks
;
1240 newbeep
->next
= NULL
;
1244 beeptail
->next
= newbeep
;
1250 * Throw out any beeps that happened more than
1254 beephead
->ticks
< ticks
- cfg
.bellovl_t
) {
1255 struct beeptime
*tmp
= beephead
;
1256 beephead
= tmp
->next
;
1263 if (cfg
.bellovl
&& beep_overloaded
&&
1264 ticks
- lastbeep
>= (unsigned)cfg
.bellovl_s
) {
1266 * If we're currently overloaded and the
1267 * last beep was more than s seconds ago,
1268 * leave overload mode.
1270 beep_overloaded
= FALSE
;
1271 } else if (cfg
.bellovl
&& !beep_overloaded
&&
1272 nbeeps
>= cfg
.bellovl_n
) {
1274 * Now, if we have n or more beeps
1275 * remaining in the queue, go into overload
1278 beep_overloaded
= TRUE
;
1283 * Perform an actual beep if we're not overloaded.
1285 if (!cfg
.bellovl
|| !beep_overloaded
) {
1287 if (cfg
.beep
== BELL_VISUAL
) {
1289 vbell_startpoint
= ticks
;
1297 if (curs
.x
== 0 && (curs
.y
== 0 || wrap
== 0));
1298 else if (curs
.x
== 0 && curs
.y
> 0)
1299 curs
.x
= cols
- 1, curs
.y
--;
1305 seen_disp_event
= TRUE
;
1308 compatibility(VT100
);
1312 compatibility(VT100
);
1317 termstate
= VT52_ESC
;
1319 compatibility(ANSIMIN
);
1320 termstate
= SEEN_ESC
;
1328 seen_disp_event
= TRUE
;
1330 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1333 if (has_compat(SCOANSI
)) {
1335 erase_lots(FALSE
, FALSE
, TRUE
);
1338 seen_disp_event
= 1;
1342 compatibility(VT100
);
1344 if (curs
.y
== marg_b
)
1345 scroll(marg_t
, marg_b
, 1, TRUE
);
1346 else if (curs
.y
< rows
- 1)
1352 seen_disp_event
= 1;
1354 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1358 pos old_curs
= curs
;
1359 unsigned long *ldata
= lineptr(curs
.y
);
1363 } while (curs
.x
< cols
- 1 && !tabs
[curs
.x
]);
1365 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
) {
1366 if (curs
.x
>= cols
/ 2)
1367 curs
.x
= cols
/ 2 - 1;
1374 check_selection(old_curs
, curs
);
1376 seen_disp_event
= TRUE
;
1380 switch (termstate
) {
1382 /* Only graphic characters get this far, ctrls are stripped above */
1383 if (wrapnext
&& wrap
) {
1384 cpos
[1] |= LATTR_WRAPPED
;
1385 if (curs
.y
== marg_b
)
1386 scroll(marg_t
, marg_b
, 1, TRUE
);
1387 else if (curs
.y
< rows
- 1)
1395 if (selstate
!= NO_SELECTION
) {
1396 pos cursplus
= curs
;
1398 check_selection(curs
, cursplus
);
1400 if ((c
& CSET_MASK
) == ATTR_ASCII
|| (c
& CSET_MASK
) == 0)
1401 logtraffic((unsigned char) c
, LGTYP_ASCII
);
1403 extern int wcwidth(wchar_t ucs
);
1408 width
= wcwidth((wchar_t) c
);
1411 *cpos
++ = c
| curr_attr
;
1412 if (++curs
.x
== cols
) {
1413 *cpos
|= LATTR_WRAPPED
;
1414 if (curs
.y
== marg_b
)
1415 scroll(marg_t
, marg_b
, 1, TRUE
);
1416 else if (curs
.y
< rows
- 1)
1421 *cpos
++ = UCSWIDE
| curr_attr
;
1424 *cpos
++ = c
| curr_attr
;
1431 if (curs
.x
== cols
) {
1435 if (wrap
&& vt52_mode
) {
1436 cpos
[1] |= LATTR_WRAPPED
;
1437 if (curs
.y
== marg_b
)
1438 scroll(marg_t
, marg_b
, 1, TRUE
);
1439 else if (curs
.y
< rows
- 1)
1446 seen_disp_event
= 1;
1451 * This state is virtually identical to SEEN_ESC, with the
1452 * exception that we have an OSC sequence in the pipeline,
1453 * and _if_ we see a backslash, we process it.
1457 termstate
= TOPLEVEL
;
1460 /* else fall through */
1462 if (c
>= ' ' && c
<= '/') {
1469 termstate
= TOPLEVEL
;
1470 switch (ANSI(c
, esc_query
)) {
1471 case '[': /* enter CSI mode */
1472 termstate
= SEEN_CSI
;
1474 esc_args
[0] = ARG_DEFAULT
;
1477 case ']': /* xterm escape sequences */
1478 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1479 compatibility(OTHER
);
1480 termstate
= SEEN_OSC
;
1483 case '7': /* save cursor */
1484 compatibility(VT100
);
1487 case '8': /* restore cursor */
1488 compatibility(VT100
);
1490 seen_disp_event
= TRUE
;
1493 compatibility(VT100
);
1494 app_keypad_keys
= TRUE
;
1497 compatibility(VT100
);
1498 app_keypad_keys
= FALSE
;
1500 case 'D': /* exactly equivalent to LF */
1501 compatibility(VT100
);
1502 if (curs
.y
== marg_b
)
1503 scroll(marg_t
, marg_b
, 1, TRUE
);
1504 else if (curs
.y
< rows
- 1)
1508 seen_disp_event
= TRUE
;
1510 case 'E': /* exactly equivalent to CR-LF */
1511 compatibility(VT100
);
1513 if (curs
.y
== marg_b
)
1514 scroll(marg_t
, marg_b
, 1, TRUE
);
1515 else if (curs
.y
< rows
- 1)
1519 seen_disp_event
= TRUE
;
1521 case 'M': /* reverse index - backwards LF */
1522 compatibility(VT100
);
1523 if (curs
.y
== marg_t
)
1524 scroll(marg_t
, marg_b
, -1, TRUE
);
1525 else if (curs
.y
> 0)
1529 seen_disp_event
= TRUE
;
1531 case 'Z': /* terminal type query */
1532 compatibility(VT100
);
1533 ldisc_send(id_string
, strlen(id_string
), 0);
1535 case 'c': /* restore power-on settings */
1536 compatibility(VT100
);
1539 if (!cfg
.no_remote_resize
)
1540 request_resize(80, rows
);
1545 seen_disp_event
= TRUE
;
1547 case 'H': /* set a tab */
1548 compatibility(VT100
);
1549 tabs
[curs
.x
] = TRUE
;
1552 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1553 compatibility(VT100
);
1555 unsigned long *ldata
;
1559 for (i
= 0; i
< rows
; i
++) {
1561 for (j
= 0; j
< cols
; j
++)
1562 ldata
[j
] = ATTR_DEFAULT
| 'E';
1566 seen_disp_event
= TRUE
;
1567 scrtop
.x
= scrtop
.y
= 0;
1570 check_selection(scrtop
, scrbot
);
1574 case ANSI('3', '#'):
1575 case ANSI('4', '#'):
1576 case ANSI('5', '#'):
1577 case ANSI('6', '#'):
1578 compatibility(VT100
);
1580 unsigned long nlattr
;
1581 unsigned long *ldata
;
1582 switch (ANSI(c
, esc_query
)) {
1583 case ANSI('3', '#'):
1586 case ANSI('4', '#'):
1589 case ANSI('5', '#'):
1590 nlattr
= LATTR_NORM
;
1592 default: /* spiritually case ANSI('6', '#'): */
1593 nlattr
= LATTR_WIDE
;
1596 ldata
= lineptr(curs
.y
);
1597 ldata
[cols
] &= ~LATTR_MODE
;
1598 ldata
[cols
] |= nlattr
;
1602 case ANSI('A', '('):
1603 compatibility(VT100
);
1604 if (!cfg
.no_remote_charset
)
1605 cset_attr
[0] = ATTR_GBCHR
;
1607 case ANSI('B', '('):
1608 compatibility(VT100
);
1609 if (!cfg
.no_remote_charset
)
1610 cset_attr
[0] = ATTR_ASCII
;
1612 case ANSI('0', '('):
1613 compatibility(VT100
);
1614 if (!cfg
.no_remote_charset
)
1615 cset_attr
[0] = ATTR_LINEDRW
;
1617 case ANSI('U', '('):
1618 compatibility(OTHER
);
1619 if (!cfg
.no_remote_charset
)
1620 cset_attr
[0] = ATTR_SCOACS
;
1623 case ANSI('A', ')'):
1624 compatibility(VT100
);
1625 if (!cfg
.no_remote_charset
)
1626 cset_attr
[1] = ATTR_GBCHR
;
1628 case ANSI('B', ')'):
1629 compatibility(VT100
);
1630 if (!cfg
.no_remote_charset
)
1631 cset_attr
[1] = ATTR_ASCII
;
1633 case ANSI('0', ')'):
1634 compatibility(VT100
);
1635 if (!cfg
.no_remote_charset
)
1636 cset_attr
[1] = ATTR_LINEDRW
;
1638 case ANSI('U', ')'):
1639 compatibility(OTHER
);
1640 if (!cfg
.no_remote_charset
)
1641 cset_attr
[1] = ATTR_SCOACS
;
1644 case ANSI('8', '%'): /* Old Linux code */
1645 case ANSI('G', '%'):
1646 compatibility(OTHER
);
1647 if (!cfg
.no_remote_charset
)
1650 case ANSI('@', '%'):
1651 compatibility(OTHER
);
1652 if (!cfg
.no_remote_charset
)
1658 termstate
= TOPLEVEL
; /* default */
1660 if (esc_nargs
<= ARGS_MAX
) {
1661 if (esc_args
[esc_nargs
- 1] == ARG_DEFAULT
)
1662 esc_args
[esc_nargs
- 1] = 0;
1663 esc_args
[esc_nargs
- 1] =
1664 10 * esc_args
[esc_nargs
- 1] + c
- '0';
1666 termstate
= SEEN_CSI
;
1667 } else if (c
== ';') {
1668 if (++esc_nargs
<= ARGS_MAX
)
1669 esc_args
[esc_nargs
- 1] = ARG_DEFAULT
;
1670 termstate
= SEEN_CSI
;
1671 } else if (c
< '@') {
1678 termstate
= SEEN_CSI
;
1680 switch (ANSI(c
, esc_query
)) {
1681 case 'A': /* move up N lines */
1682 move(curs
.x
, curs
.y
- def(esc_args
[0], 1), 1);
1683 seen_disp_event
= TRUE
;
1685 case 'e': /* move down N lines */
1686 compatibility(ANSI
);
1689 move(curs
.x
, curs
.y
+ def(esc_args
[0], 1), 1);
1690 seen_disp_event
= TRUE
;
1692 case ANSI('c', '>'): /* report xterm version */
1693 compatibility(OTHER
);
1694 /* this reports xterm version 136 so that VIM can
1695 use the drag messages from the mouse reporting */
1696 ldisc_send("\033[>0;136;0c", 11, 0);
1698 case 'a': /* move right N cols */
1699 compatibility(ANSI
);
1702 move(curs
.x
+ def(esc_args
[0], 1), curs
.y
, 1);
1703 seen_disp_event
= TRUE
;
1705 case 'D': /* move left N cols */
1706 move(curs
.x
- def(esc_args
[0], 1), curs
.y
, 1);
1707 seen_disp_event
= TRUE
;
1709 case 'E': /* move down N lines and CR */
1710 compatibility(ANSI
);
1711 move(0, curs
.y
+ def(esc_args
[0], 1), 1);
1712 seen_disp_event
= TRUE
;
1714 case 'F': /* move up N lines and CR */
1715 compatibility(ANSI
);
1716 move(0, curs
.y
- def(esc_args
[0], 1), 1);
1717 seen_disp_event
= TRUE
;
1720 case '`': /* set horizontal posn */
1721 compatibility(ANSI
);
1722 move(def(esc_args
[0], 1) - 1, curs
.y
, 0);
1723 seen_disp_event
= TRUE
;
1725 case 'd': /* set vertical posn */
1726 compatibility(ANSI
);
1728 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1731 seen_disp_event
= TRUE
;
1734 case 'f': /* set horz and vert posns at once */
1736 esc_args
[1] = ARG_DEFAULT
;
1737 move(def(esc_args
[1], 1) - 1,
1738 (dec_om ? marg_t
: 0) + def(esc_args
[0],
1741 seen_disp_event
= TRUE
;
1743 case 'J': /* erase screen or parts of it */
1745 unsigned int i
= def(esc_args
[0], 0) + 1;
1748 erase_lots(FALSE
, !!(i
& 2), !!(i
& 1));
1751 seen_disp_event
= TRUE
;
1753 case 'K': /* erase line or parts of it */
1755 unsigned int i
= def(esc_args
[0], 0) + 1;
1758 erase_lots(TRUE
, !!(i
& 2), !!(i
& 1));
1760 seen_disp_event
= TRUE
;
1762 case 'L': /* insert lines */
1763 compatibility(VT102
);
1764 if (curs
.y
<= marg_b
)
1765 scroll(curs
.y
, marg_b
, -def(esc_args
[0], 1),
1768 seen_disp_event
= TRUE
;
1770 case 'M': /* delete lines */
1771 compatibility(VT102
);
1772 if (curs
.y
<= marg_b
)
1773 scroll(curs
.y
, marg_b
, def(esc_args
[0], 1),
1776 seen_disp_event
= TRUE
;
1778 case '@': /* insert chars */
1779 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1780 compatibility(VT102
);
1781 insch(def(esc_args
[0], 1));
1782 seen_disp_event
= TRUE
;
1784 case 'P': /* delete chars */
1785 compatibility(VT102
);
1786 insch(-def(esc_args
[0], 1));
1787 seen_disp_event
= TRUE
;
1789 case 'c': /* terminal type query */
1790 compatibility(VT100
);
1791 /* This is the response for a VT102 */
1792 ldisc_send(id_string
, strlen(id_string
), 0);
1794 case 'n': /* cursor position query */
1795 if (esc_args
[0] == 6) {
1797 sprintf(buf
, "\033[%d;%dR", curs
.y
+ 1,
1799 ldisc_send(buf
, strlen(buf
), 0);
1800 } else if (esc_args
[0] == 5) {
1801 ldisc_send("\033[0n", 4, 0);
1804 case 'h': /* toggle modes to high */
1806 compatibility(VT100
);
1809 for (i
= 0; i
< esc_nargs
; i
++)
1810 toggle_mode(esc_args
[i
], esc_query
, TRUE
);
1813 case 'l': /* toggle modes to low */
1815 compatibility(VT100
);
1818 for (i
= 0; i
< esc_nargs
; i
++)
1819 toggle_mode(esc_args
[i
], esc_query
, FALSE
);
1822 case 'g': /* clear tabs */
1823 compatibility(VT100
);
1824 if (esc_nargs
== 1) {
1825 if (esc_args
[0] == 0) {
1826 tabs
[curs
.x
] = FALSE
;
1827 } else if (esc_args
[0] == 3) {
1829 for (i
= 0; i
< cols
; i
++)
1834 case 'r': /* set scroll margins */
1835 compatibility(VT100
);
1836 if (esc_nargs
<= 2) {
1838 top
= def(esc_args
[0], 1) - 1;
1839 bot
= (esc_nargs
<= 1
1841 0 ? rows
: def(esc_args
[1], rows
)) - 1;
1844 /* VTTEST Bug 9 - if region is less than 2 lines
1845 * don't change region.
1847 if (bot
- top
> 0) {
1852 * I used to think the cursor should be
1853 * placed at the top of the newly marginned
1854 * area. Apparently not: VMS TPU falls over
1857 * Well actually it should for Origin mode - RDB
1859 curs
.y
= (dec_om ? marg_t
: 0);
1861 seen_disp_event
= TRUE
;
1865 case 'm': /* set graphics rendition */
1868 * A VT100 without the AVO only had one attribute, either
1869 * underline or reverse video depending on the cursor type,
1870 * this was selected by CSI 7m.
1873 * This is sometimes DIM, eg on the GIGI and Linux
1875 * This is sometimes INVIS various ANSI.
1877 * This like 22 disables BOLD, DIM and INVIS
1879 * The ANSI colours appear on any terminal that has colour
1880 * (obviously) but the interaction between sgr0 and the
1881 * colours varies but is usually related to the background
1882 * colour erase item.
1883 * The interaction between colour attributes and the mono
1884 * ones is also very implementation dependent.
1886 * The 39 and 49 attributes are likely to be unimplemented.
1889 for (i
= 0; i
< esc_nargs
; i
++) {
1890 switch (def(esc_args
[i
], 0)) {
1891 case 0: /* restore defaults */
1892 curr_attr
= ATTR_DEFAULT
;
1894 case 1: /* enable bold */
1895 compatibility(VT100AVO
);
1896 curr_attr
|= ATTR_BOLD
;
1898 case 21: /* (enable double underline) */
1899 compatibility(OTHER
);
1900 case 4: /* enable underline */
1901 compatibility(VT100AVO
);
1902 curr_attr
|= ATTR_UNDER
;
1904 case 5: /* enable blink */
1905 compatibility(VT100AVO
);
1906 curr_attr
|= ATTR_BLINK
;
1908 case 7: /* enable reverse video */
1909 curr_attr
|= ATTR_REVERSE
;
1911 case 10: /* SCO acs off */
1912 compatibility(SCOANSI
);
1913 if (cfg
.no_remote_charset
) break;
1915 case 11: /* SCO acs on */
1916 compatibility(SCOANSI
);
1917 if (cfg
.no_remote_charset
) break;
1919 case 12: /* SCO acs on flipped */
1920 compatibility(SCOANSI
);
1921 if (cfg
.no_remote_charset
) break;
1923 case 22: /* disable bold */
1924 compatibility2(OTHER
, VT220
);
1925 curr_attr
&= ~ATTR_BOLD
;
1927 case 24: /* disable underline */
1928 compatibility2(OTHER
, VT220
);
1929 curr_attr
&= ~ATTR_UNDER
;
1931 case 25: /* disable blink */
1932 compatibility2(OTHER
, VT220
);
1933 curr_attr
&= ~ATTR_BLINK
;
1935 case 27: /* disable reverse video */
1936 compatibility2(OTHER
, VT220
);
1937 curr_attr
&= ~ATTR_REVERSE
;
1948 curr_attr
&= ~ATTR_FGMASK
;
1950 (esc_args
[i
] - 30) << ATTR_FGSHIFT
;
1952 case 39: /* default-foreground */
1953 curr_attr
&= ~ATTR_FGMASK
;
1954 curr_attr
|= ATTR_DEFFG
;
1965 curr_attr
&= ~ATTR_BGMASK
;
1967 (esc_args
[i
] - 40) << ATTR_BGSHIFT
;
1969 case 49: /* default-background */
1970 curr_attr
&= ~ATTR_BGMASK
;
1971 curr_attr
|= ATTR_DEFBG
;
1976 erase_char
= (' ' | ATTR_ASCII
|
1978 (ATTR_FGMASK
| ATTR_BGMASK
)));
1981 case 's': /* save cursor */
1984 case 'u': /* restore cursor */
1986 seen_disp_event
= TRUE
;
1988 case 't': /* set page size - ie window height */
1990 * VT340/VT420 sequence DECSLPP, DEC only allows values
1991 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1992 * illegal values (eg first arg 1..9) for window changing
1996 && (esc_args
[0] < 1 || esc_args
[0] >= 24)) {
1997 compatibility(VT340TEXT
);
1998 if (!cfg
.no_remote_resize
)
1999 request_resize(cols
, def(esc_args
[0], 24));
2001 } else if (esc_nargs
>= 1 &&
2004 compatibility(OTHER
);
2006 switch (esc_args
[0]) {
2016 if (esc_nargs
>= 3) {
2017 if (!cfg
.no_remote_resize
)
2018 move_window(def(esc_args
[1], 0),
2019 def(esc_args
[2], 0));
2023 /* We should resize the window to a given
2024 * size in pixels here, but currently our
2025 * resizing code isn't healthy enough to
2029 set_zorder(TRUE
); /* move to top */
2032 set_zorder(FALSE
); /* move to bottom */
2038 if (esc_nargs
>= 3) {
2039 if (!cfg
.no_remote_resize
)
2040 request_resize(def(esc_args
[2], cfg
.width
),
2041 def(esc_args
[1], cfg
.height
));
2046 set_zoomed(esc_args
[1] ? TRUE
: FALSE
);
2049 ldisc_send(is_iconic() ?
"\033[1t" : "\033[2t",
2053 get_window_pos(&x
, &y
);
2054 len
= sprintf(buf
, "\033[3;%d;%dt", x
, y
);
2055 ldisc_send(buf
, len
, 0);
2058 get_window_pixels(&x
, &y
);
2059 len
= sprintf(buf
, "\033[4;%d;%dt", x
, y
);
2060 ldisc_send(buf
, len
, 0);
2063 len
= sprintf(buf
, "\033[8;%d;%dt",
2065 ldisc_send(buf
, len
, 0);
2069 * Hmmm. Strictly speaking we
2070 * should return `the size of the
2071 * screen in characters', but
2072 * that's not easy: (a) window
2073 * furniture being what it is it's
2074 * hard to compute, and (b) in
2075 * resize-font mode maximising the
2076 * window wouldn't change the
2077 * number of characters. *shrug*. I
2078 * think we'll ignore it for the
2079 * moment and see if anyone
2080 * complains, and then ask them
2081 * what they would like it to do.
2085 p
= get_window_title(TRUE
);
2087 ldisc_send("\033]L", 3, 0);
2088 ldisc_send(p
, len
, 0);
2089 ldisc_send("\033\\", 2, 0);
2092 p
= get_window_title(FALSE
);
2094 ldisc_send("\033]l", 3, 0);
2095 ldisc_send(p
, len
, 0);
2096 ldisc_send("\033\\", 2, 0);
2102 compatibility(SCOANSI
);
2103 scroll(marg_t
, marg_b
, def(esc_args
[0], 1), TRUE
);
2106 seen_disp_event
= TRUE
;
2109 compatibility(SCOANSI
);
2110 scroll(marg_t
, marg_b
, -def(esc_args
[0], 1), TRUE
);
2113 seen_disp_event
= TRUE
;
2115 case ANSI('|', '*'):
2116 /* VT420 sequence DECSNLS
2117 * Set number of lines on screen
2118 * VT420 uses VGA like hardware and can support any size in
2119 * reasonable range (24..49 AIUI) with no default specified.
2121 compatibility(VT420
);
2122 if (esc_nargs
== 1 && esc_args
[0] > 0) {
2123 if (!cfg
.no_remote_resize
)
2124 request_resize(cols
, def(esc_args
[0], cfg
.height
));
2128 case ANSI('|', '$'):
2129 /* VT340/VT420 sequence DECSCPP
2130 * Set number of columns per page
2131 * Docs imply range is only 80 or 132, but I'll allow any.
2133 compatibility(VT340TEXT
);
2134 if (esc_nargs
<= 1) {
2135 if (!cfg
.no_remote_resize
)
2136 request_resize(def(esc_args
[0], cfg
.width
), rows
);
2140 case 'X': /* write N spaces w/o moving cursor */
2141 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2142 compatibility(ANSIMIN
);
2144 int n
= def(esc_args
[0], 1);
2146 unsigned long *p
= cpos
;
2147 if (n
> cols
- curs
.x
)
2151 check_selection(curs
, cursplus
);
2154 seen_disp_event
= TRUE
;
2157 case 'x': /* report terminal characteristics */
2158 compatibility(VT100
);
2161 int i
= def(esc_args
[0], 0);
2162 if (i
== 0 || i
== 1) {
2163 strcpy(buf
, "\033[2;1;1;112;112;1;0x");
2165 ldisc_send(buf
, 20, 0);
2169 case 'Z': /* BackTab for xterm */
2170 compatibility(OTHER
);
2172 int i
= def(esc_args
[0], 1);
2173 pos old_curs
= curs
;
2175 for(;i
>0 && curs
.x
>0; i
--) {
2178 } while (curs
.x
>0 && !tabs
[curs
.x
]);
2181 check_selection(old_curs
, curs
);
2184 case ANSI('L', '='):
2185 compatibility(OTHER
);
2186 use_bce
= (esc_args
[0] <= 0);
2187 erase_char
= ERASE_CHAR
;
2189 erase_char
= (' ' | ATTR_ASCII
|
2191 (ATTR_FGMASK
| ATTR_BGMASK
)));
2193 case ANSI('E', '='):
2194 compatibility(OTHER
);
2195 blink_is_real
= (esc_args
[0] >= 1);
2197 case ANSI('p', '"'):
2198 /* Allow the host to make this emulator a 'perfect' VT102.
2199 * This first appeared in the VT220, but we do need to get
2200 * back to PuTTY mode so I won't check it.
2202 * The arg in 40..42,50 are a PuTTY extension.
2203 * The 2nd arg, 8bit vs 7bit is not checked.
2205 * Setting VT102 mode should also change the Fkeys to
2206 * generate PF* codes as a real VT102 has no Fkeys.
2207 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2210 * Note ESC c will NOT change this!
2213 switch (esc_args
[0]) {
2215 compatibility_level
&= ~TM_VTXXX
;
2216 compatibility_level
|= TM_VT102
;
2219 compatibility_level
&= ~TM_VTXXX
;
2220 compatibility_level
|= TM_VT220
;
2224 if (esc_args
[0] > 60 && esc_args
[0] < 70)
2225 compatibility_level
|= TM_VTXXX
;
2229 compatibility_level
&= TM_VTXXX
;
2232 compatibility_level
= TM_PUTTY
;
2235 compatibility_level
= TM_SCOANSI
;
2239 compatibility_level
= TM_PUTTY
;
2245 /* Change the response to CSI c */
2246 if (esc_args
[0] == 50) {
2249 strcpy(id_string
, "\033[?");
2250 for (i
= 1; i
< esc_nargs
; i
++) {
2252 strcat(id_string
, ";");
2253 sprintf(lbuf
, "%d", esc_args
[i
]);
2254 strcat(id_string
, lbuf
);
2256 strcat(id_string
, "c");
2259 /* Is this a good idea ?
2260 * Well we should do a soft reset at this point ...
2262 if (!has_compat(VT420
) && has_compat(VT100
)) {
2263 if (!cfg
.no_remote_resize
) {
2265 request_resize(132, 24);
2267 request_resize(80, 24);
2277 case 'P': /* Linux palette sequence */
2278 termstate
= SEEN_OSC_P
;
2281 case 'R': /* Linux palette reset */
2284 termstate
= TOPLEVEL
;
2286 case 'W': /* word-set */
2287 termstate
= SEEN_OSC_W
;
2300 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2304 * Grotty hack to support xterm and DECterm title
2305 * sequences concurrently.
2307 if (esc_args
[0] == 2) {
2311 /* else fall through */
2313 termstate
= OSC_STRING
;
2319 * This OSC stuff is EVIL. It takes just one character to get into
2320 * sysline mode and it's not initially obvious how to get out.
2321 * So I've added CR and LF as string aborts.
2322 * This shouldn't effect compatibility as I believe embedded
2323 * control characters are supposed to be interpreted (maybe?)
2324 * and they don't display anything useful anyway.
2328 if (c
== '\n' || c
== '\r') {
2329 termstate
= TOPLEVEL
;
2330 } else if (c
== 0234 || c
== '\007') {
2332 * These characters terminate the string; ST and BEL
2333 * terminate the sequence and trigger instant
2334 * processing of it, whereas ESC goes back to SEEN_ESC
2335 * mode unless it is followed by \, in which case it is
2336 * synonymous with ST in the first place.
2339 termstate
= TOPLEVEL
;
2340 } else if (c
== '\033')
2341 termstate
= OSC_MAYBE_ST
;
2342 else if (osc_strlen
< OSC_STR_MAX
)
2343 osc_string
[osc_strlen
++] = c
;
2347 int max
= (osc_strlen
== 0 ?
21 : 16);
2349 if (c
>= '0' && c
<= '9')
2351 else if (c
>= 'A' && c
<= 'A' + max
- 10)
2353 else if (c
>= 'a' && c
<= 'a' + max
- 10)
2356 termstate
= TOPLEVEL
;
2359 osc_string
[osc_strlen
++] = val
;
2360 if (osc_strlen
>= 7) {
2361 palette_set(osc_string
[0],
2362 osc_string
[1] * 16 + osc_string
[2],
2363 osc_string
[3] * 16 + osc_string
[4],
2364 osc_string
[5] * 16 + osc_string
[6]);
2366 termstate
= TOPLEVEL
;
2382 esc_args
[0] = 10 * esc_args
[0] + c
- '0';
2385 termstate
= OSC_STRING
;
2390 termstate
= TOPLEVEL
;
2391 seen_disp_event
= TRUE
;
2394 move(curs
.x
, curs
.y
- 1, 1);
2397 move(curs
.x
, curs
.y
+ 1, 1);
2400 move(curs
.x
+ 1, curs
.y
, 1);
2403 move(curs
.x
- 1, curs
.y
, 1);
2406 * From the VT100 Manual
2407 * NOTE: The special graphics characters in the VT100
2408 * are different from those in the VT52
2410 * From VT102 manual:
2411 * 137 _ Blank - Same
2412 * 140 ` Reserved - Humm.
2413 * 141 a Solid rectangle - Similar
2414 * 142 b 1/ - Top half of fraction for the
2415 * 143 c 3/ - subscript numbers below.
2418 * 146 f Degrees - Same
2419 * 147 g Plus or minus - Same
2421 * 151 i Ellipsis (dots)
2424 * 154 l Bar at scan 0
2425 * 155 m Bar at scan 1
2426 * 156 n Bar at scan 2
2427 * 157 o Bar at scan 3 - Similar
2428 * 160 p Bar at scan 4 - Similar
2429 * 161 q Bar at scan 5 - Similar
2430 * 162 r Bar at scan 6 - Same
2431 * 163 s Bar at scan 7 - Similar
2446 cset_attr
[cset
= 0] = ATTR_LINEDRW
;
2449 cset_attr
[cset
= 0] = ATTR_ASCII
;
2456 scroll(0, rows
- 1, -1, TRUE
);
2457 else if (curs
.y
> 0)
2463 erase_lots(FALSE
, FALSE
, TRUE
);
2467 erase_lots(TRUE
, FALSE
, TRUE
);
2471 /* XXX Print cursor line */
2474 /* XXX Start controller mode */
2477 /* XXX Stop controller mode */
2481 termstate
= VT52_Y1
;
2484 ldisc_send("\033/Z", 3, 0);
2487 app_keypad_keys
= TRUE
;
2490 app_keypad_keys
= FALSE
;
2493 /* XXX This should switch to VT100 mode not current or default
2494 * VT mode. But this will only have effect in a VT220+
2498 blink_is_real
= cfg
.blinktext
;
2502 /* XXX Enter auto print mode */
2505 /* XXX Exit auto print mode */
2508 /* XXX Print screen */
2514 /* compatibility(ATARI) */
2516 erase_lots(FALSE
, FALSE
, TRUE
);
2520 /* compatibility(ATARI) */
2521 if (curs
.y
<= marg_b
)
2522 scroll(curs
.y
, marg_b
, -1, FALSE
);
2525 /* compatibility(ATARI) */
2526 if (curs
.y
<= marg_b
)
2527 scroll(curs
.y
, marg_b
, 1, TRUE
);
2530 /* compatibility(ATARI) */
2531 termstate
= VT52_FG
;
2534 /* compatibility(ATARI) */
2535 termstate
= VT52_BG
;
2538 /* compatibility(ATARI) */
2539 erase_lots(FALSE
, TRUE
, FALSE
);
2543 /* compatibility(ATARI) */
2547 /* compatibility(ATARI) */
2550 /* case 'j': Save cursor position - broken on ST */
2551 /* case 'k': Restore cursor position */
2553 /* compatibility(ATARI) */
2554 erase_lots(TRUE
, TRUE
, TRUE
);
2560 /* compatibility(ATARI) */
2561 erase_lots(TRUE
, TRUE
, FALSE
);
2564 /* compatibility(ATARI) */
2565 curr_attr
|= ATTR_REVERSE
;
2568 /* compatibility(ATARI) */
2569 curr_attr
&= ~ATTR_REVERSE
;
2571 case 'v': /* wrap Autowrap on - Wyse style */
2572 /* compatibility(ATARI) */
2575 case 'w': /* Autowrap off */
2576 /* compatibility(ATARI) */
2581 /* compatibility(OTHER) */
2583 curr_attr
= ATTR_DEFAULT
;
2585 erase_char
= (' ' | ATTR_ASCII
|
2587 (ATTR_FGMASK
| ATTR_BGMASK
)));
2590 /* compatibility(VI50) */
2591 curr_attr
|= ATTR_UNDER
;
2594 /* compatibility(VI50) */
2595 curr_attr
&= ~ATTR_UNDER
;
2598 /* compatibility(VI50) */
2600 curr_attr
|= ATTR_BOLD
;
2603 /* compatibility(VI50) */
2605 curr_attr
&= ~ATTR_BOLD
;
2611 termstate
= VT52_Y2
;
2612 move(curs
.x
, c
- ' ', 0);
2615 termstate
= TOPLEVEL
;
2616 move(c
- ' ', curs
.y
, 0);
2621 termstate
= TOPLEVEL
;
2622 curr_attr
&= ~ATTR_FGMASK
;
2623 curr_attr
&= ~ATTR_BOLD
;
2624 curr_attr
|= (c
& 0x7) << ATTR_FGSHIFT
;
2625 if ((c
& 0x8) || vt52_bold
)
2626 curr_attr
|= ATTR_BOLD
;
2629 erase_char
= (' ' | ATTR_ASCII
|
2630 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2633 termstate
= TOPLEVEL
;
2634 curr_attr
&= ~ATTR_BGMASK
;
2635 curr_attr
&= ~ATTR_BLINK
;
2636 curr_attr
|= (c
& 0x7) << ATTR_BGSHIFT
;
2638 /* Note: bold background */
2640 curr_attr
|= ATTR_BLINK
;
2643 erase_char
= (' ' | ATTR_ASCII
|
2644 (curr_attr
& (ATTR_FGMASK
| ATTR_BGMASK
)));
2647 default: break; /* placate gcc warning about enum use */
2649 if (selstate
!= NO_SELECTION
) {
2650 pos cursplus
= curs
;
2652 check_selection(curs
, cursplus
);
2659 * Compare two lines to determine whether they are sufficiently
2660 * alike to scroll-optimise one to the other. Return the degree of
2663 static int linecmp(unsigned long *a
, unsigned long *b
)
2667 for (i
= n
= 0; i
< cols
; i
++)
2668 n
+= (*a
++ == *b
++);
2674 * Given a context, update the window. Out of paranoia, we don't
2675 * allow WM_PAINT responses to do scrolling optimisations.
2677 static void do_paint(Context ctx
, int may_optimise
)
2679 int i
, j
, our_curs_y
;
2680 unsigned long rv
, cursor
;
2683 long cursor_background
= ERASE_CHAR
;
2684 unsigned long ticks
;
2687 * Check the visual bell state.
2690 ticks
= GetTickCount();
2691 if (ticks
- vbell_startpoint
>= VBELL_TIMEOUT
)
2695 rv
= (!rvideo
^ !in_vbell ? ATTR_REVERSE
: 0);
2698 * screen array, disptop, scrtop,
2700 * cfg.blinkpc, blink_is_real, tblinker,
2701 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2704 /* Has the cursor position or type changed ? */
2707 if (blinker
|| !cfg
.blink_cur
)
2708 cursor
= TATTR_ACTCURS
;
2712 cursor
= TATTR_PASCURS
;
2714 cursor
|= TATTR_RIGHTCURS
;
2717 our_curs_y
= curs
.y
- disptop
;
2719 if (dispcurs
&& (curstype
!= cursor
||
2721 disptext
+ our_curs_y
* (cols
+ 1) + curs
.x
)) {
2722 if (dispcurs
> disptext
&&
2723 (*dispcurs
& (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2724 dispcurs
[-1] |= ATTR_INVALID
;
2725 if ( (dispcurs
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2726 dispcurs
[1] |= ATTR_INVALID
;
2727 *dispcurs
|= ATTR_INVALID
;
2732 /* The normal screen data */
2733 for (i
= 0; i
< rows
; i
++) {
2734 unsigned long *ldata
;
2736 int idx
, dirty_line
, dirty_run
, selected
;
2737 unsigned long attr
= 0;
2738 int updated_line
= 0;
2741 int last_run_dirty
= 0;
2743 scrpos
.y
= i
+ disptop
;
2744 ldata
= lineptr(scrpos
.y
);
2745 lattr
= (ldata
[cols
] & LATTR_MODE
);
2747 idx
= i
* (cols
+ 1);
2748 dirty_run
= dirty_line
= (ldata
[cols
] != disptext
[idx
+ cols
]);
2749 disptext
[idx
+ cols
] = ldata
[cols
];
2751 for (j
= 0; j
< cols
; j
++, idx
++) {
2752 unsigned long tattr
, tchar
;
2753 unsigned long *d
= ldata
+ j
;
2757 tchar
= (*d
& (CHAR_MASK
| CSET_MASK
));
2758 tattr
= (*d
& (ATTR_MASK
^ CSET_MASK
));
2759 switch (tchar
& CSET_MASK
) {
2761 tchar
= unitab_line
[tchar
& 0xFF];
2764 tchar
= unitab_xterm
[tchar
& 0xFF];
2767 tchar
= unitab_scoacs
[tchar
&0xFF];
2770 tattr
|= (tchar
& CSET_MASK
);
2772 if ((d
[1] & (CHAR_MASK
| CSET_MASK
)) == UCSWIDE
)
2775 /* Video reversing things */
2776 if (seltype
== LEXICOGRAPHIC
)
2777 selected
= posle(selstart
, scrpos
) && poslt(scrpos
, selend
);
2779 selected
= posPle(selstart
, scrpos
) && posPlt(scrpos
, selend
);
2781 ^ (selected ? ATTR_REVERSE
: 0));
2783 /* 'Real' blinking ? */
2784 if (blink_is_real
&& (tattr
& ATTR_BLINK
)) {
2785 if (has_focus
&& tblinker
) {
2787 tattr
&= ~CSET_MASK
;
2790 tattr
&= ~ATTR_BLINK
;
2794 * Check the font we'll _probably_ be using to see if
2795 * the character is wide when we don't want it to be.
2797 if ((tchar
| tattr
) != (disptext
[idx
]& ~ATTR_NARROW
)) {
2798 if ((tattr
& ATTR_WIDE
) == 0 &&
2799 CharWidth(ctx
, (tchar
| tattr
) & 0xFFFF) == 2)
2800 tattr
|= ATTR_NARROW
;
2801 } else if (disptext
[idx
]&ATTR_NARROW
)
2802 tattr
|= ATTR_NARROW
;
2804 /* Cursor here ? Save the 'background' */
2805 if (i
== our_curs_y
&& j
== curs
.x
) {
2806 cursor_background
= tattr
| tchar
;
2807 dispcurs
= disptext
+ idx
;
2810 if ((disptext
[idx
] ^ tattr
) & ATTR_WIDE
)
2813 break_run
= (tattr
!= attr
|| j
- start
>= sizeof(ch
));
2815 /* Special hack for VT100 Linedraw glyphs */
2816 if ((attr
& CSET_MASK
) == 0x2300 && tchar
>= 0xBA
2817 && tchar
<= 0xBD) break_run
= TRUE
;
2819 if (!dbcs_screenfont
&& !dirty_line
) {
2820 if ((tchar
| tattr
) == disptext
[idx
])
2822 else if (!dirty_run
&& ccount
== 1)
2827 if ((dirty_run
|| last_run_dirty
) && ccount
> 0) {
2828 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2834 if (dbcs_screenfont
)
2835 last_run_dirty
= dirty_run
;
2836 dirty_run
= dirty_line
;
2839 if ((tchar
| tattr
) != disptext
[idx
])
2841 ch
[ccount
++] = (char) tchar
;
2842 disptext
[idx
] = tchar
| tattr
;
2844 /* If it's a wide char step along to the next one. */
2845 if (tattr
& ATTR_WIDE
) {
2849 /* Cursor is here ? Ouch! */
2850 if (i
== our_curs_y
&& j
== curs
.x
) {
2851 cursor_background
= *d
;
2852 dispcurs
= disptext
+ idx
;
2854 if (disptext
[idx
] != *d
)
2860 if (dirty_run
&& ccount
> 0) {
2861 do_text(ctx
, start
, i
, ch
, ccount
, attr
, lattr
);
2865 /* Cursor on this line ? (and changed) */
2866 if (i
== our_curs_y
&& (curstype
!= cursor
|| updated_line
)) {
2867 ch
[0] = (char) (cursor_background
& CHAR_MASK
);
2868 attr
= (cursor_background
& ATTR_MASK
) | cursor
;
2869 do_cursor(ctx
, curs
.x
, i
, ch
, 1, attr
, lattr
);
2876 * Flick the switch that says if blinking things should be shown or hidden.
2879 void term_blink(int flg
)
2881 static long last_blink
= 0;
2882 static long last_tblink
= 0;
2883 long now
, blink_diff
;
2885 now
= GetTickCount();
2886 blink_diff
= now
- last_tblink
;
2888 /* Make sure the text blinks no more than 2Hz */
2889 if (blink_diff
< 0 || blink_diff
> 450) {
2891 tblinker
= !tblinker
;
2900 blink_diff
= now
- last_blink
;
2902 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2903 if (blink_diff
>= 0 && blink_diff
< (long) GetCaretBlinkTime())
2911 * Invalidate the whole screen so it will be repainted in full.
2913 void term_invalidate(void)
2917 for (i
= 0; i
< rows
* (cols
+ 1); i
++)
2918 disptext
[i
] = ATTR_INVALID
;
2922 * Paint the window in response to a WM_PAINT message.
2924 void term_paint(Context ctx
, int left
, int top
, int right
, int bottom
)
2927 if (left
< 0) left
= 0;
2928 if (top
< 0) top
= 0;
2929 if (right
>= cols
) right
= cols
-1;
2930 if (bottom
>= rows
) bottom
= rows
-1;
2932 for (i
= top
; i
<= bottom
&& i
< rows
; i
++) {
2933 if ((disptext
[i
* (cols
+ 1) + cols
] & LATTR_MODE
) == LATTR_NORM
)
2934 for (j
= left
; j
<= right
&& j
< cols
; j
++)
2935 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2937 for (j
= left
/ 2; j
<= right
/ 2 + 1 && j
< cols
; j
++)
2938 disptext
[i
* (cols
+ 1) + j
] = ATTR_INVALID
;
2941 /* This should happen soon enough, also for some reason it sometimes
2942 * fails to actually do anything when re-sizing ... painting the wrong
2946 do_paint (ctx
, FALSE
);
2950 * Attempt to scroll the scrollback. The second parameter gives the
2951 * position we want to scroll to; the first is +1 to denote that
2952 * this position is relative to the beginning of the scrollback, -1
2953 * to denote it is relative to the end, and 0 to denote that it is
2954 * relative to the current position.
2956 void term_scroll(int rel
, int where
)
2958 int sbtop
= -count234(scrollback
);
2960 disptop
= (rel
< 0 ?
0 : rel
> 0 ? sbtop
: disptop
) + where
;
2961 if (disptop
< sbtop
)
2969 static void clipme(pos top
, pos bottom
, int rect
)
2972 wchar_t *wbptr
; /* where next char goes within workbuf */
2974 int wblen
= 0; /* workbuf len */
2975 int buflen
; /* amount of memory allocated to workbuf */
2977 buflen
= 5120; /* Default size */
2978 workbuf
= smalloc(buflen
* sizeof(wchar_t));
2979 wbptr
= workbuf
; /* start filling here */
2980 old_top_x
= top
.x
; /* needed for rect==1 */
2982 while (poslt(top
, bottom
)) {
2984 unsigned long *ldata
= lineptr(top
.y
);
2988 * nlpos will point at the maximum position on this line we
2989 * should copy up to. So we start it at the end of the
2996 * ... move it backwards if there's unused space at the end
2997 * of the line (and also set `nl' if this is the case,
2998 * because in normal selection mode this means we need a
2999 * newline at the end)...
3001 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
3002 while (((ldata
[nlpos
.x
- 1] & 0xFF) == 0x20 ||
3003 (DIRECT_CHAR(ldata
[nlpos
.x
- 1]) &&
3004 (ldata
[nlpos
.x
- 1] & CHAR_MASK
) == 0x20))
3005 && poslt(top
, nlpos
))
3007 if (poslt(nlpos
, bottom
))
3012 * ... and then clip it to the terminal x coordinate if
3013 * we're doing rectangular selection. (In this case we
3014 * still did the above, so that copying e.g. the right-hand
3015 * column from a table doesn't fill with spaces on the
3019 if (nlpos
.x
> bottom
.x
)
3021 nl
= (top
.y
< bottom
.y
);
3024 while (poslt(top
, bottom
) && poslt(top
, nlpos
)) {
3027 sprintf(cbuf
, "<U+%04x>", (ldata
[top
.x
] & 0xFFFF));
3029 wchar_t cbuf
[16], *p
;
3030 int uc
= (ldata
[top
.x
] & 0xFFFF);
3033 if (uc
== UCSWIDE
) {
3038 switch (uc
& CSET_MASK
) {
3041 uc
= unitab_xterm
[uc
& 0xFF];
3045 uc
= unitab_line
[uc
& 0xFF];
3048 uc
= unitab_scoacs
[uc
&0xFF];
3051 switch (uc
& CSET_MASK
) {
3053 uc
= unitab_font
[uc
& 0xFF];
3056 uc
= unitab_oemcp
[uc
& 0xFF];
3060 set
= (uc
& CSET_MASK
);
3061 c
= (uc
& CHAR_MASK
);
3065 if (DIRECT_FONT(uc
)) {
3066 if (c
>= ' ' && c
!= 0x7F) {
3067 unsigned char buf
[4];
3070 if (IsDBCSLeadByteEx(font_codepage
, (BYTE
) c
)) {
3072 buf
[1] = (unsigned char) ldata
[top
.x
+ 1];
3073 rv
= MultiByteToWideChar(font_codepage
,
3074 0, buf
, 2, wbuf
, 4);
3078 rv
= MultiByteToWideChar(font_codepage
,
3079 0, buf
, 1, wbuf
, 4);
3083 memcpy(cbuf
, wbuf
, rv
* sizeof(wchar_t));
3090 for (p
= cbuf
; *p
; p
++) {
3091 /* Enough overhead for trailing NL and nul */
3092 if (wblen
>= buflen
- 16) {
3095 sizeof(wchar_t) * (buflen
+= 100));
3096 wbptr
= workbuf
+ wblen
;
3105 for (i
= 0; i
< sel_nl_sz
; i
++) {
3107 *wbptr
++ = sel_nl
[i
];
3111 top
.x
= rect ? old_top_x
: 0;
3115 write_clip(workbuf
, wblen
, FALSE
); /* transfer to clipboard */
3116 if (buflen
> 0) /* indicates we allocated this buffer */
3120 void term_copyall(void)
3123 top
.y
= -count234(scrollback
);
3125 clipme(top
, curs
, 0);
3129 * The wordness array is mainly for deciding the disposition of the US-ASCII
3132 static int wordtype(int uc
)
3135 int start
, end
, ctype
;
3136 } *wptr
, ucs_words
[] = {
3142 0x037e, 0x037e, 1}, /* Greek question mark */
3144 0x0387, 0x0387, 1}, /* Greek ano teleia */
3146 0x055a, 0x055f, 1}, /* Armenian punctuation */
3148 0x0589, 0x0589, 1}, /* Armenian full stop */
3150 0x0700, 0x070d, 1}, /* Syriac punctuation */
3152 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3154 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3156 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3158 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3160 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3162 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3164 0x2000, 0x200a, 0}, /* Various spaces */
3166 0x2070, 0x207f, 2}, /* superscript */
3168 0x2080, 0x208f, 2}, /* subscript */
3170 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3172 0x3000, 0x3000, 0}, /* ideographic space */
3174 0x3001, 0x3020, 1}, /* ideographic punctuation */
3176 0x303f, 0x309f, 3}, /* Hiragana */
3178 0x30a0, 0x30ff, 3}, /* Katakana */
3180 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3182 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3184 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3186 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3188 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3190 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3192 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3194 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3196 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3201 uc
&= (CSET_MASK
| CHAR_MASK
);
3203 switch (uc
& CSET_MASK
) {
3205 uc
= unitab_xterm
[uc
& 0xFF];
3208 uc
= unitab_line
[uc
& 0xFF];
3211 uc
= unitab_scoacs
[uc
&0xFF];
3214 switch (uc
& CSET_MASK
) {
3216 uc
= unitab_font
[uc
& 0xFF];
3219 uc
= unitab_oemcp
[uc
& 0xFF];
3223 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3224 * fail as there's such a thing as a double width space. :-(
3226 if (dbcs_screenfont
&& font_codepage
== line_codepage
)
3230 return wordness
[uc
];
3232 for (wptr
= ucs_words
; wptr
->start
; wptr
++) {
3233 if (uc
>= wptr
->start
&& uc
<= wptr
->end
)
3241 * Spread the selection outwards according to the selection mode.
3243 static pos
sel_spread_half(pos p
, int dir
)
3245 unsigned long *ldata
;
3247 int topy
= -count234(scrollback
);
3249 ldata
= lineptr(p
.y
);
3254 * In this mode, every character is a separate unit, except
3255 * for runs of spaces at the end of a non-wrapping line.
3257 if (!(ldata
[cols
] & LATTR_WRAPPED
)) {
3258 unsigned long *q
= ldata
+ cols
;
3259 while (q
> ldata
&& (q
[-1] & CHAR_MASK
) == 0x20)
3261 if (q
== ldata
+ cols
)
3263 if (p
.x
>= q
- ldata
)
3264 p
.x
= (dir
== -1 ? q
- ldata
: cols
- 1);
3269 * In this mode, the units are maximal runs of characters
3270 * whose `wordness' has the same value.
3272 wvalue
= wordtype(ldata
[p
.x
]);
3276 if (wordtype(ldata
[p
.x
+ 1]) == wvalue
)
3281 if (ldata
[cols
] & LATTR_WRAPPED
) {
3282 unsigned long *ldata2
;
3283 ldata2
= lineptr(p
.y
+1);
3284 if (wordtype(ldata2
[0]) == wvalue
) {
3297 if (wordtype(ldata
[p
.x
- 1]) == wvalue
)
3302 unsigned long *ldata2
;
3305 ldata2
= lineptr(p
.y
-1);
3306 if ((ldata2
[cols
] & LATTR_WRAPPED
) &&
3307 wordtype(ldata2
[cols
-1]) == wvalue
) {
3319 * In this mode, every line is a unit.
3321 p
.x
= (dir
== -1 ?
0 : cols
- 1);
3327 static void sel_spread(void)
3329 if (seltype
== LEXICOGRAPHIC
) {
3330 selstart
= sel_spread_half(selstart
, -1);
3332 selend
= sel_spread_half(selend
, +1);
3337 void term_do_paste(void)
3342 get_clip(&data
, &len
);
3347 sfree(paste_buffer
);
3348 paste_pos
= paste_hold
= paste_len
= 0;
3349 paste_buffer
= smalloc(len
* sizeof(wchar_t));
3352 while (p
< data
+ len
) {
3353 while (p
< data
+ len
&&
3354 !(p
<= data
+ len
- sel_nl_sz
&&
3355 !memcmp(p
, sel_nl
, sizeof(sel_nl
))))
3360 for (i
= 0; i
< p
- q
; i
++) {
3361 paste_buffer
[paste_len
++] = q
[i
];
3365 if (p
<= data
+ len
- sel_nl_sz
&&
3366 !memcmp(p
, sel_nl
, sizeof(sel_nl
))) {
3367 paste_buffer
[paste_len
++] = '\r';
3373 /* Assume a small paste will be OK in one go. */
3374 if (paste_len
< 256) {
3375 luni_send(paste_buffer
, paste_len
, 0);
3377 sfree(paste_buffer
);
3379 paste_pos
= paste_hold
= paste_len
= 0;
3382 get_clip(NULL
, NULL
);
3385 void term_mouse(Mouse_Button b
, Mouse_Action a
, int x
, int y
,
3386 int shift
, int ctrl
, int alt
)
3389 unsigned long *ldata
;
3390 int raw_mouse
= xterm_mouse
&& !(cfg
.mouse_override
&& shift
);
3391 int default_seltype
;
3395 if (a
== MA_DRAG
&& !raw_mouse
)
3400 if (a
== MA_DRAG
&& !raw_mouse
)
3413 selpoint
.y
= y
+ disptop
;
3415 ldata
= lineptr(selpoint
.y
);
3416 if ((ldata
[cols
] & LATTR_MODE
) != LATTR_NORM
)
3420 int encstate
= 0, r
, c
;
3422 static int is_down
= 0;
3426 encstate
= 0x20; /* left button down */
3437 case MBT_WHEEL_DOWN
:
3440 default: break; /* placate gcc warning about enum use */
3444 if (xterm_mouse
== 1)
3457 default: break; /* placate gcc warning about enum use */
3466 sprintf(abuf
, "\033[M%c%c%c", encstate
, c
, r
);
3467 ldisc_send(abuf
, 6, 0);
3471 b
= translate_button(b
);
3474 * Set the selection type (rectangular or normal) at the start
3475 * of a selection attempt, from the state of Alt.
3477 if (!alt
^ !cfg
.rect_select
)
3478 default_seltype
= RECTANGULAR
;
3480 default_seltype
= LEXICOGRAPHIC
;
3482 if (selstate
== NO_SELECTION
) {
3483 seltype
= default_seltype
;
3486 if (b
== MBT_SELECT
&& a
== MA_CLICK
) {
3488 selstate
= ABOUT_TO
;
3489 seltype
= default_seltype
;
3490 selanchor
= selpoint
;
3492 } else if (b
== MBT_SELECT
&& (a
== MA_2CLK
|| a
== MA_3CLK
)) {
3494 selmode
= (a
== MA_2CLK ? SM_WORD
: SM_LINE
);
3495 selstate
= DRAGGING
;
3496 selstart
= selanchor
= selpoint
;
3500 } else if ((b
== MBT_SELECT
&& a
== MA_DRAG
) ||
3501 (b
== MBT_EXTEND
&& a
!= MA_RELEASE
)) {
3502 if (selstate
== ABOUT_TO
&& poseq(selanchor
, selpoint
))
3504 if (b
== MBT_EXTEND
&& a
!= MA_DRAG
&& selstate
== SELECTED
) {
3505 if (seltype
== LEXICOGRAPHIC
) {
3507 * For normal selection, we extend by moving
3508 * whichever end of the current selection is closer
3511 if (posdiff(selpoint
, selstart
) <
3512 posdiff(selend
, selstart
) / 2) {
3516 selanchor
= selstart
;
3520 * For rectangular selection, we have a choice of
3521 * _four_ places to put selanchor and selpoint: the
3522 * four corners of the selection.
3524 if (2*selpoint
.x
< selstart
.x
+ selend
.x
)
3525 selanchor
.x
= selend
.x
-1;
3527 selanchor
.x
= selstart
.x
;
3529 if (2*selpoint
.y
< selstart
.y
+ selend
.y
)
3530 selanchor
.y
= selend
.y
;
3532 selanchor
.y
= selstart
.y
;
3534 selstate
= DRAGGING
;
3536 if (selstate
!= ABOUT_TO
&& selstate
!= DRAGGING
)
3537 selanchor
= selpoint
;
3538 selstate
= DRAGGING
;
3539 if (seltype
== LEXICOGRAPHIC
) {
3541 * For normal selection, we set (selstart,selend) to
3542 * (selpoint,selanchor) in some order.
3544 if (poslt(selpoint
, selanchor
)) {
3545 selstart
= selpoint
;
3549 selstart
= selanchor
;
3555 * For rectangular selection, we may need to
3556 * interchange x and y coordinates (if the user has
3557 * dragged in the -x and +y directions, or vice versa).
3559 selstart
.x
= min(selanchor
.x
, selpoint
.x
);
3560 selend
.x
= 1+max(selanchor
.x
, selpoint
.x
);
3561 selstart
.y
= min(selanchor
.y
, selpoint
.y
);
3562 selend
.y
= max(selanchor
.y
, selpoint
.y
);
3565 } else if ((b
== MBT_SELECT
|| b
== MBT_EXTEND
) && a
== MA_RELEASE
) {
3566 if (selstate
== DRAGGING
) {
3568 * We've completed a selection. We now transfer the
3569 * data to the clipboard.
3571 clipme(selstart
, selend
, (seltype
== RECTANGULAR
));
3572 selstate
= SELECTED
;
3574 selstate
= NO_SELECTION
;
3575 } else if (b
== MBT_PASTE
3576 && (a
== MA_CLICK
|| a
== MA_2CLK
|| a
== MA_3CLK
)) {
3587 sfree(paste_buffer
);
3594 static long last_paste
= 0;
3595 long now
, paste_diff
;
3600 /* Don't wait forever to paste */
3602 now
= GetTickCount();
3603 paste_diff
= now
- last_paste
;
3604 if (paste_diff
>= 0 && paste_diff
< 450)
3609 while (paste_pos
< paste_len
) {
3611 while (n
+ paste_pos
< paste_len
) {
3612 if (paste_buffer
[paste_pos
+ n
++] == '\r')
3615 luni_send(paste_buffer
+ paste_pos
, n
, 0);
3618 if (paste_pos
< paste_len
) {
3623 sfree(paste_buffer
);
3628 static void deselect(void)
3630 selstate
= NO_SELECTION
;
3631 selstart
.x
= selstart
.y
= selend
.x
= selend
.y
= 0;
3634 void term_deselect(void)
3640 int term_ldisc(int option
)
3642 if (option
== LD_ECHO
)
3643 return term_echoing
;
3644 if (option
== LD_EDIT
)
3645 return term_editing
;
3650 * from_backend(), to get data from the backend for the terminal.
3652 int from_backend(int is_stderr
, char *data
, int len
)
3656 bufchain_add(&inbuf
, data
, len
);
3659 * term_out() always completely empties inbuf. Therefore,
3660 * there's no reason at all to return anything other than zero
3661 * from this function, because there _can't_ be a question of
3662 * the remote side needing to wait until term_out() has cleared
3665 * This is a slightly suboptimal way to deal with SSH2 - in
3666 * principle, the window mechanism would allow us to continue
3667 * to accept data on forwarded ports and X connections even
3668 * while the terminal processing was going slowly - but we
3669 * can't do the 100% right thing without moving the terminal
3670 * processing into a separate thread, and that might hurt
3671 * portability. So we manage stdout buffering the old SSH1 way:
3672 * if the terminal processing goes slowly, the whole SSH
3673 * connection stops accepting data until it's ready.
3675 * In practice, I can't imagine this causing serious trouble.