10 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
11 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
12 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
13 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (term->cols+1) + (p1).x - (p2).x )
15 /* Product-order comparisons for rectangular block selection. */
16 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
17 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
19 #define incpos(p) ( (p).x == term->cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
20 #define decpos(p) ( (p).x == 0 ? ((p).x = term->cols, (p).y--, 1) : ((p).x--, 0) )
24 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
25 #define CL_VT100 0x0002 /* VT100 */
26 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
27 #define CL_VT102 0x0008 /* VT102 */
28 #define CL_VT220 0x0010 /* VT220 */
29 #define CL_VT320 0x0020 /* VT320 */
30 #define CL_VT420 0x0040 /* VT420 */
31 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
32 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
33 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
34 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
35 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
37 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
38 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
39 #define TM_VT102 (TM_VT100AVO|CL_VT102)
40 #define TM_VT220 (TM_VT102|CL_VT220)
41 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
42 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
44 #define TM_PUTTY (0xFFFF)
46 #define compatibility(x) \
47 if ( ((CL_##x)&term->compatibility_level) == 0 ) { \
48 term->termstate=TOPLEVEL; \
51 #define compatibility2(x,y) \
52 if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \
53 term->termstate=TOPLEVEL; \
57 #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
59 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
60 const wchar_t sel_nl[] = SEL_NL;
63 * Internal prototypes.
65 static void do_paint(Terminal *, Context, int);
66 static void erase_lots(Terminal *, int, int, int);
67 static void swap_screen(Terminal *, int, int, int);
68 static void update_sbar(Terminal *);
69 static void deselect(Terminal *);
70 static void term_print_finish(Terminal *);
73 * Resize a line to make it `cols' columns wide.
75 unsigned long *resizeline(unsigned long *line, int cols)
78 unsigned long lineattrs;
80 if (line[0] != (unsigned long)cols) {
82 * This line is the wrong length, which probably means it
83 * hasn't been accessed since a resize. Resize it now.
86 lineattrs = line[oldlen + 1];
87 line = srealloc(line, TSIZE * (2 + cols));
89 for (i = oldlen; i < cols; i++)
90 line[i + 1] = ERASE_CHAR;
91 line[cols + 1] = lineattrs & LATTR_MODE;
98 * Retrieve a line of the screen or of the scrollback, according to
99 * whether the y coordinate is non-negative or negative
102 unsigned long *lineptr(Terminal *term, int y, int lineno)
104 unsigned long *line, *newline;
109 whichtree = term->screen;
112 whichtree = term->scrollback;
113 treeindex = y + count234(term->scrollback);
115 line = index234(whichtree, treeindex);
117 /* We assume that we don't screw up and retrieve something out of range. */
118 assert(line != NULL);
120 newline = resizeline(line, term->cols);
121 if (newline != line) {
122 delpos234(whichtree, treeindex);
123 addpos234(whichtree, newline, treeindex);
130 #define lineptr(x) lineptr(term,x,__LINE__)
133 * Set up power-on settings for the terminal.
135 static void power_on(Terminal *term)
137 term->curs.x = term->curs.y = 0;
138 term->alt_x = term->alt_y = 0;
139 term->savecurs.x = term->savecurs.y = 0;
140 term->alt_t = term->marg_t = 0;
141 if (term->rows != -1)
142 term->alt_b = term->marg_b = term->rows - 1;
144 term->alt_b = term->marg_b = 0;
145 if (term->cols != -1) {
147 for (i = 0; i < term->cols; i++)
148 term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
150 term->alt_om = term->dec_om = cfg.dec_om;
151 term->alt_ins = term->insert = FALSE;
152 term->alt_wnext = term->wrapnext = term->save_wnext = FALSE;
153 term->alt_wrap = term->wrap = cfg.wrap_mode;
154 term->alt_cset = term->cset = term->save_cset = 0;
155 term->alt_utf = term->utf = term->save_utf = 0;
157 term->alt_sco_acs = term->sco_acs = term->save_sco_acs = 0;
158 term->cset_attr[0] = term->cset_attr[1] = term->save_csattr = ATTR_ASCII;
160 term->in_vbell = FALSE;
162 term->big_cursor = 0;
163 term->save_attr = term->curr_attr = ATTR_DEFAULT;
164 term->term_editing = term->term_echoing = FALSE;
165 term->app_cursor_keys = cfg.app_cursor;
166 term->app_keypad_keys = cfg.app_keypad;
167 term->use_bce = cfg.bce;
168 term->blink_is_real = cfg.blinktext;
169 term->erase_char = ERASE_CHAR;
171 term_print_finish(term);
174 for (i = 0; i < 256; i++)
175 term->wordness[i] = cfg.wordness[i];
178 swap_screen(term, 1, FALSE, FALSE);
179 erase_lots(term, FALSE, TRUE, TRUE);
180 swap_screen(term, 0, FALSE, FALSE);
181 erase_lots(term, FALSE, TRUE, TRUE);
186 * Force a screen update.
188 void term_update(Terminal *term)
193 int need_sbar_update = term->seen_disp_event;
194 if (term->seen_disp_event && cfg.scroll_on_disp) {
195 term->disptop = 0; /* return to main screen */
196 term->seen_disp_event = 0;
197 need_sbar_update = TRUE;
199 if (need_sbar_update)
201 do_paint(term, ctx, TRUE);
202 sys_cursor(term->curs.x, term->curs.y - term->disptop);
208 * Called from front end when a keypress occurs, to trigger
209 * anything magical that needs to happen in that situation.
211 void term_seen_key_event(Terminal *term)
214 * On any keypress, clear the bell overload mechanism
215 * completely, on the grounds that large numbers of
216 * beeps coming from deliberate key action are likely
217 * to be intended (e.g. beeps from filename completion
218 * blocking repeatedly).
220 term->beep_overloaded = FALSE;
221 while (term->beephead) {
222 struct beeptime *tmp = term->beephead;
223 term->beephead = tmp->next;
226 term->beeptail = NULL;
230 * Reset the scrollback on keypress, if we're doing that.
232 if (cfg.scroll_on_key) {
233 term->disptop = 0; /* return to main screen */
234 term->seen_disp_event = 1;
239 * Same as power_on(), but an external function.
241 void term_pwron(Terminal *term)
244 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
252 * When the user reconfigures us, we need to check the forbidden-
253 * alternate-screen config option, disable raw mouse mode if the
254 * user has disabled mouse reporting, and abandon a print job if
255 * the user has disabled printing.
257 void term_reconfig(Terminal *term)
259 if (cfg.no_alt_screen)
260 swap_screen(term, 0, FALSE, FALSE);
261 if (cfg.no_mouse_rep) {
262 term->xterm_mouse = 0;
263 set_raw_mouse_mode(0);
265 if (cfg.no_remote_charset) {
266 term->cset_attr[0] = term->cset_attr[1] = ATTR_ASCII;
267 term->sco_acs = term->alt_sco_acs = 0;
271 term_print_finish(term);
276 * Clear the scrollback.
278 void term_clrsb(Terminal *term)
282 while ((line = delpos234(term->scrollback, 0)) != NULL) {
289 * Initialise the terminal.
291 Terminal *term_init(void)
296 * Allocate a new Terminal structure and initialise the fields
299 term = smalloc(sizeof(Terminal));
300 term->compatibility_level = TM_PUTTY;
301 strcpy(term->id_string, "\033[?6c");
302 term->last_blink = term->last_tblink = 0;
303 term->paste_buffer = NULL;
304 term->last_paste = 0;
305 bufchain_init(&term->inbuf);
306 bufchain_init(&term->printer_buf);
307 term->printing = term->only_printing = FALSE;
308 term->print_job = NULL;
309 term->vt52_mode = FALSE;
310 term->cr_lf_return = FALSE;
311 term->seen_disp_event = FALSE;
312 term->xterm_mouse = FALSE;
313 term->reset_132 = FALSE;
314 term->blinker = term->tblinker = 0;
316 term->repeat_off = FALSE;
317 term->termstate = TOPLEVEL;
318 term->selstate = NO_SELECTION;
320 term->screen = term->alt_screen = term->scrollback = NULL;
322 term->disptext = term->dispcurs = NULL;
325 term->rows = term->cols = -1;
327 term->beephead = term->beeptail = NULL;
329 term->lastbeep = FALSE;
330 term->beep_overloaded = FALSE;
331 term->resize_fn = NULL;
332 term->resize_ctx = NULL;
338 * Set up the terminal for a given size.
340 void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
343 unsigned long *newdisp, *line;
346 int save_alt_which = term->alt_which;
348 if (newrows == term->rows && newcols == term->cols &&
349 newsavelines == term->savelines)
350 return; /* nothing to do */
353 swap_screen(term, 0, FALSE, FALSE);
355 term->alt_t = term->marg_t = 0;
356 term->alt_b = term->marg_b = newrows - 1;
358 if (term->rows == -1) {
359 term->scrollback = newtree234(NULL);
360 term->screen = newtree234(NULL);
365 * Resize the screen and scrollback. We only need to shift
366 * lines around within our data structures, because lineptr()
367 * will take care of resizing each individual line if
370 * - If the new screen and the old screen differ in length, we
371 * must shunt some lines in from the scrollback or out to
374 * - If doing that fails to provide us with enough material to
375 * fill the new screen (i.e. the number of rows needed in
376 * the new screen exceeds the total number in the previous
377 * screen+scrollback), we must invent some blank lines to
380 * - Then, if the new scrollback length is less than the
381 * amount of scrollback we actually have, we must throw some
384 sblen = count234(term->scrollback);
385 /* Do this loop to expand the screen if newrows > rows */
386 for (i = term->rows; i < newrows; i++) {
388 line = delpos234(term->scrollback, --sblen);
390 line = smalloc(TSIZE * (newcols + 2));
392 for (j = 0; j <= newcols; j++)
393 line[j + 1] = ERASE_CHAR;
395 addpos234(term->screen, line, 0);
397 /* Do this loop to shrink the screen if newrows < rows */
398 for (i = newrows; i < term->rows; i++) {
399 line = delpos234(term->screen, 0);
400 addpos234(term->scrollback, line, sblen++);
402 assert(count234(term->screen) == newrows);
403 while (sblen > newsavelines) {
404 line = delpos234(term->scrollback, 0);
408 assert(count234(term->scrollback) <= newsavelines);
411 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
412 for (i = 0; i < newrows * (newcols + 1); i++)
413 newdisp[i] = ATTR_INVALID;
414 sfree(term->disptext);
415 term->disptext = newdisp;
416 term->dispcurs = NULL;
418 newalt = newtree234(NULL);
419 for (i = 0; i < newrows; i++) {
420 line = smalloc(TSIZE * (newcols + 2));
422 for (j = 0; j <= newcols; j++)
423 line[j + 1] = term->erase_char;
424 addpos234(newalt, line, i);
426 if (term->alt_screen) {
427 while (NULL != (line = delpos234(term->alt_screen, 0)))
429 freetree234(term->alt_screen);
431 term->alt_screen = newalt;
433 term->tabs = srealloc(term->tabs, newcols * sizeof(*term->tabs));
436 for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++)
437 term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
441 term->curs.y += newrows - term->rows;
442 if (term->curs.y < 0)
444 if (term->curs.y >= newrows)
445 term->curs.y = newrows - 1;
446 if (term->curs.x >= newcols)
447 term->curs.x = newcols - 1;
448 term->alt_x = term->alt_y = 0;
449 term->wrapnext = term->alt_wnext = FALSE;
451 term->rows = newrows;
452 term->cols = newcols;
453 term->savelines = newsavelines;
456 swap_screen(term, save_alt_which, FALSE, FALSE);
461 term->resize_fn(term->resize_ctx, term->cols, term->rows);
465 * Hand a function and context pointer to the terminal which it can
466 * use to notify a back end of resizes.
468 void term_provide_resize_fn(Terminal *term,
469 void (*resize_fn)(void *, int, int),
472 term->resize_fn = resize_fn;
473 term->resize_ctx = resize_ctx;
474 if (term->cols > 0 && term->rows > 0)
475 resize_fn(resize_ctx, term->cols, term->rows);
479 * Swap screens. If `reset' is TRUE and we have been asked to
480 * switch to the alternate screen, we must bring most of its
481 * configuration from the main screen and erase the contents of the
482 * alternate screen completely. (This is even true if we're already
483 * on it! Blame xterm.)
485 static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)
491 reset = FALSE; /* do no weird resetting if which==0 */
493 if (which != term->alt_which) {
494 term->alt_which = which;
496 ttr = term->alt_screen;
497 term->alt_screen = term->screen;
500 if (!reset && !keep_cur_pos)
501 term->curs.x = term->alt_x;
504 if (!reset && !keep_cur_pos)
505 term->curs.y = term->alt_y;
508 if (!reset) term->marg_t = term->alt_t;
511 if (!reset) term->marg_b = term->alt_b;
514 if (!reset) term->dec_om = term->alt_om;
517 if (!reset) term->wrap = term->alt_wrap;
520 if (!reset) term->wrapnext = term->alt_wnext;
523 if (!reset) term->insert = term->alt_ins;
526 if (!reset) term->cset = term->alt_cset;
529 if (!reset) term->utf = term->alt_utf;
532 if (!reset) term->sco_acs = term->alt_sco_acs;
533 term->alt_sco_acs = t;
536 if (reset && term->screen) {
538 * Yes, this _is_ supposed to honour background-colour-erase.
540 erase_lots(term, FALSE, TRUE, TRUE);
544 * This might not be possible if we're called during
552 * Update the scroll bar.
554 static void update_sbar(Terminal *term)
558 nscroll = count234(term->scrollback);
560 set_sbar(nscroll + term->rows, nscroll + term->disptop, term->rows);
564 * Check whether the region bounded by the two pointers intersects
565 * the scroll region, and de-select the on-screen selection if so.
567 static void check_selection(Terminal *term, pos from, pos to)
569 if (poslt(from, term->selend) && poslt(term->selstart, to))
574 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
575 * for backward.) `sb' is TRUE if the scrolling is permitted to
576 * affect the scrollback buffer.
578 * NB this function invalidates all pointers into lines of the
579 * screen data structures. In particular, you MUST call fix_cpos
580 * after calling scroll() and before doing anything else that
581 * uses the cpos shortcut pointer.
583 static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
585 unsigned long *line, *line2;
588 if (topline != 0 || term->alt_which != 0)
593 line = delpos234(term->screen, botline);
594 line = resizeline(line, term->cols);
595 for (i = 0; i < term->cols; i++)
596 line[i + 1] = term->erase_char;
597 line[term->cols + 1] = 0;
598 addpos234(term->screen, line, topline);
600 if (term->selstart.y >= topline && term->selstart.y <= botline) {
602 if (term->selstart.y > botline) {
603 term->selstart.y = botline;
604 term->selstart.x = 0;
607 if (term->selend.y >= topline && term->selend.y <= botline) {
609 if (term->selend.y > botline) {
610 term->selend.y = botline;
619 line = delpos234(term->screen, topline);
620 if (sb && term->savelines > 0) {
621 int sblen = count234(term->scrollback);
623 * We must add this line to the scrollback. We'll
624 * remove a line from the top of the scrollback to
625 * replace it, or allocate a new one if the
626 * scrollback isn't full.
628 if (sblen == term->savelines) {
629 sblen--, line2 = delpos234(term->scrollback, 0);
631 line2 = smalloc(TSIZE * (term->cols + 2));
632 line2[0] = term->cols;
634 addpos234(term->scrollback, line, sblen);
638 * If the user is currently looking at part of the
639 * scrollback, and they haven't enabled any options
640 * that are going to reset the scrollback as a
641 * result of this movement, then the chances are
642 * they'd like to keep looking at the same line. So
643 * we move their viewpoint at the same rate as the
644 * scroll, at least until their viewpoint hits the
645 * top end of the scrollback buffer, at which point
646 * we don't have the choice any more.
648 * Thanks to Jan Holmen Holsten for the idea and
649 * initial implementation.
651 if (term->disptop > -term->savelines && term->disptop < 0)
654 line = resizeline(line, term->cols);
655 for (i = 0; i < term->cols; i++)
656 line[i + 1] = term->erase_char;
657 line[term->cols + 1] = 0;
658 addpos234(term->screen, line, botline);
661 * If the selection endpoints move into the scrollback,
662 * we keep them moving until they hit the top. However,
663 * of course, if the line _hasn't_ moved into the
664 * scrollback then we don't do this, and cut them off
665 * at the top of the scroll region.
667 * This applies to selstart and selend (for an existing
668 * selection), and also selanchor (for one being
669 * selected as we speak).
671 seltop = sb ? -term->savelines : topline;
673 if (term->selstart.y >= seltop &&
674 term->selstart.y <= botline) {
676 if (term->selstart.y < seltop) {
677 term->selstart.y = seltop;
678 term->selstart.x = 0;
681 if (term->selend.y >= seltop && term->selend.y <= botline) {
683 if (term->selend.y < seltop) {
684 term->selend.y = seltop;
688 if (term->selanchor.y >= seltop && term->selanchor.y <= botline) {
690 if (term->selanchor.y < seltop) {
691 term->selanchor.y = seltop;
692 term->selanchor.x = 0;
702 * Move the cursor to a given position, clipping at boundaries. We
703 * may or may not want to clip at the scroll margin: marg_clip is 0
704 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
705 * even _being_ outside the margins.
707 static void move(Terminal *term, int x, int y, int marg_clip)
714 if ((term->curs.y >= term->marg_t || marg_clip == 2) &&
717 if ((term->curs.y <= term->marg_b || marg_clip == 2) &&
728 term->wrapnext = FALSE;
732 * Save or restore the cursor and SGR mode.
734 static void save_cursor(Terminal *term, int save)
737 term->savecurs = term->curs;
738 term->save_attr = term->curr_attr;
739 term->save_cset = term->cset;
740 term->save_utf = term->utf;
741 term->save_wnext = term->wrapnext;
742 term->save_csattr = term->cset_attr[term->cset];
743 term->save_sco_acs = term->sco_acs;
745 term->curs = term->savecurs;
746 /* Make sure the window hasn't shrunk since the save */
747 if (term->curs.x >= term->cols)
748 term->curs.x = term->cols - 1;
749 if (term->curs.y >= term->rows)
750 term->curs.y = term->rows - 1;
752 term->curr_attr = term->save_attr;
753 term->cset = term->save_cset;
754 term->utf = term->save_utf;
755 term->wrapnext = term->save_wnext;
757 * wrapnext might reset to False if the x position is no
758 * longer at the rightmost edge.
760 if (term->wrapnext && term->curs.x < term->cols-1)
761 term->wrapnext = FALSE;
762 term->cset_attr[term->cset] = term->save_csattr;
763 term->sco_acs = term->save_sco_acs;
766 term->erase_char = (' ' | ATTR_ASCII |
768 (ATTR_FGMASK | ATTR_BGMASK)));
773 * Erase a large portion of the screen: the whole screen, or the
774 * whole line, or parts thereof.
776 static void erase_lots(Terminal *term,
777 int line_only, int from_begin, int to_end)
781 unsigned long *ldata;
784 start.y = term->curs.y;
786 end.y = term->curs.y + 1;
803 check_selection(term, start, end);
805 /* Clear screen also forces a full window redraw, just in case. */
806 if (start.y == 0 && start.x == 0 && end.y == term->rows)
807 term_invalidate(term);
809 ldata = lineptr(start.y);
810 while (poslt(start, end)) {
811 if (start.x == term->cols && !erase_lattr)
812 ldata[start.x] &= ~LATTR_WRAPPED;
814 ldata[start.x] = term->erase_char;
815 if (incpos(start) && start.y < term->rows)
816 ldata = lineptr(start.y);
821 * Insert or delete characters within the current line. n is +ve if
822 * insertion is desired, and -ve for deletion.
824 static void insch(Terminal *term, int n)
826 int dir = (n < 0 ? -1 : +1);
829 unsigned long *ldata;
831 n = (n < 0 ? -n : n);
832 if (n > term->cols - term->curs.x)
833 n = term->cols - term->curs.x;
834 m = term->cols - term->curs.x - n;
835 cursplus.y = term->curs.y;
836 cursplus.x = term->curs.x + n;
837 check_selection(term, term->curs, cursplus);
838 ldata = lineptr(term->curs.y);
840 memmove(ldata + term->curs.x, ldata + term->curs.x + n, m * TSIZE);
842 ldata[term->curs.x + m++] = term->erase_char;
844 memmove(ldata + term->curs.x + n, ldata + term->curs.x, m * TSIZE);
846 ldata[term->curs.x + n] = term->erase_char;
851 * Toggle terminal mode `mode' to state `state'. (`query' indicates
852 * whether the mode is a DEC private one or a normal one.)
854 static void toggle_mode(Terminal *term, int mode, int query, int state)
860 case 1: /* application cursor keys */
861 term->app_cursor_keys = state;
863 case 2: /* VT52 mode */
864 term->vt52_mode = !state;
865 if (term->vt52_mode) {
866 term->blink_is_real = FALSE;
867 term->vt52_bold = FALSE;
869 term->blink_is_real = cfg.blinktext;
872 case 3: /* 80/132 columns */
874 if (!cfg.no_remote_resize)
875 request_resize(state ? 132 : 80, term->rows);
876 term->reset_132 = state;
878 case 5: /* reverse video */
880 * Toggle reverse video. If we receive an OFF within the
881 * visual bell timeout period after an ON, we trigger an
882 * effective visual bell, so that ESC[?5hESC[?5l will
883 * always be an actually _visible_ visual bell.
885 ticks = GETTICKCOUNT();
886 /* turn off a previous vbell to avoid inconsistencies */
887 if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
888 term->in_vbell = FALSE;
889 if (term->rvideo && !state && /* we're turning it off... */
890 (ticks - term->rvbell_startpoint) < VBELL_TIMEOUT) {/*...soon*/
891 /* If there's no vbell timeout already, or this one lasts
892 * longer, replace vbell_timeout with ours. */
893 if (!term->in_vbell ||
894 (term->rvbell_startpoint - term->vbell_startpoint <
896 term->vbell_startpoint = term->rvbell_startpoint;
897 term->in_vbell = TRUE; /* may clear rvideo but set in_vbell */
898 } else if (!term->rvideo && state) {
899 /* This is an ON, so we notice the time and save it. */
900 term->rvbell_startpoint = ticks;
902 term->rvideo = state;
903 term->seen_disp_event = TRUE;
907 case 6: /* DEC origin mode */
908 term->dec_om = state;
910 case 7: /* auto wrap */
913 case 8: /* auto key repeat */
914 term->repeat_off = !state;
916 case 10: /* set local edit mode */
917 term->term_editing = state;
918 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
920 case 25: /* enable/disable cursor */
921 compatibility2(OTHER, VT220);
922 term->cursor_on = state;
923 term->seen_disp_event = TRUE;
925 case 47: /* alternate screen */
926 compatibility(OTHER);
928 swap_screen(term, cfg.no_alt_screen ? 0 : state, FALSE, FALSE);
931 case 1000: /* xterm mouse 1 */
932 term->xterm_mouse = state ? 1 : 0;
933 set_raw_mouse_mode(state);
935 case 1002: /* xterm mouse 2 */
936 term->xterm_mouse = state ? 2 : 0;
937 set_raw_mouse_mode(state);
939 case 1047: /* alternate screen */
940 compatibility(OTHER);
942 swap_screen(term, cfg.no_alt_screen ? 0 : state, TRUE, TRUE);
945 case 1048: /* save/restore cursor */
946 save_cursor(term, state);
947 if (!state) term->seen_disp_event = TRUE;
949 case 1049: /* cursor & alternate screen */
951 save_cursor(term, state);
952 if (!state) term->seen_disp_event = TRUE;
953 compatibility(OTHER);
955 swap_screen(term, cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
957 save_cursor(term, state);
962 case 4: /* set insert mode */
963 compatibility(VT102);
964 term->insert = state;
966 case 12: /* set echo mode */
967 term->term_echoing = !state;
968 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
970 case 20: /* Return sends ... */
971 term->cr_lf_return = state;
973 case 34: /* Make cursor BIG */
974 compatibility2(OTHER, VT220);
975 term->big_cursor = !state;
980 * Process an OSC sequence: set window title or icon name.
982 static void do_osc(Terminal *term)
985 while (term->osc_strlen--)
986 term->wordness[(unsigned char)
987 term->osc_string[term->osc_strlen]] = term->esc_args[0];
989 term->osc_string[term->osc_strlen] = '\0';
990 switch (term->esc_args[0]) {
993 if (!cfg.no_remote_wintitle)
994 set_icon(term->osc_string);
995 if (term->esc_args[0] == 1)
997 /* fall through: parameter 0 means set both */
1000 if (!cfg.no_remote_wintitle)
1001 set_title(term->osc_string);
1008 * ANSI printing routines.
1010 static void term_print_setup(Terminal *term)
1012 bufchain_clear(&term->printer_buf);
1013 term->print_job = printer_start_job(cfg.printer);
1015 static void term_print_flush(Terminal *term)
1020 while ((size = bufchain_size(&term->printer_buf)) > 5) {
1021 bufchain_prefix(&term->printer_buf, &data, &len);
1024 printer_job_data(term->print_job, data, len);
1025 bufchain_consume(&term->printer_buf, len);
1028 static void term_print_finish(Terminal *term)
1034 if (!term->printing && !term->only_printing)
1035 return; /* we need do nothing */
1037 term_print_flush(term);
1038 while ((size = bufchain_size(&term->printer_buf)) > 0) {
1039 bufchain_prefix(&term->printer_buf, &data, &len);
1041 if (c == '\033' || c == '\233') {
1042 bufchain_consume(&term->printer_buf, size);
1045 printer_job_data(term->print_job, &c, 1);
1046 bufchain_consume(&term->printer_buf, 1);
1049 printer_finish_job(term->print_job);
1050 term->print_job = NULL;
1051 term->printing = term->only_printing = FALSE;
1055 * Remove everything currently in `inbuf' and stick it up on the
1056 * in-memory display. There's a big state machine in here to
1057 * process escape sequences...
1059 void term_out(Terminal *term)
1062 unsigned char localbuf[256], *chars;
1067 chars = NULL; /* placate compiler warnings */
1068 while (nchars > 0 || bufchain_size(&term->inbuf) > 0) {
1072 bufchain_prefix(&term->inbuf, &ret, &nchars);
1073 if (nchars > sizeof(localbuf))
1074 nchars = sizeof(localbuf);
1075 memcpy(localbuf, ret, nchars);
1076 bufchain_consume(&term->inbuf, nchars);
1078 assert(chars != NULL);
1084 * Optionally log the session traffic to a file. Useful for
1085 * debugging and possibly also useful for actual logging.
1087 if (cfg.logtype == LGTYP_DEBUG)
1088 logtraffic((unsigned char) c, LGTYP_DEBUG);
1094 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1095 * be able to display 8-bit characters, but I'll let that go 'cause
1100 * If we're printing, add the character to the printer
1103 if (term->printing) {
1104 bufchain_add(&term->printer_buf, &c, 1);
1107 * If we're in print-only mode, we use a much simpler
1108 * state machine designed only to recognise the ESC[4i
1109 * termination sequence.
1111 if (term->only_printing) {
1113 term->print_state = 1;
1114 else if (c == (unsigned char)'\233')
1115 term->print_state = 2;
1116 else if (c == '[' && term->print_state == 1)
1117 term->print_state = 2;
1118 else if (c == '4' && term->print_state == 2)
1119 term->print_state = 3;
1120 else if (c == 'i' && term->print_state == 3)
1121 term->print_state = 4;
1123 term->print_state = 0;
1124 if (term->print_state == 4) {
1125 term_print_finish(term);
1131 /* First see about all those translations. */
1132 if (term->termstate == TOPLEVEL) {
1134 switch (term->utf_state) {
1137 /* UTF-8 must be stateless so we ignore iso2022. */
1138 if (unitab_ctrl[c] != 0xFF)
1140 else c = ((unsigned char)c) | ATTR_ASCII;
1142 } else if ((c & 0xe0) == 0xc0) {
1143 term->utf_size = term->utf_state = 1;
1144 term->utf_char = (c & 0x1f);
1145 } else if ((c & 0xf0) == 0xe0) {
1146 term->utf_size = term->utf_state = 2;
1147 term->utf_char = (c & 0x0f);
1148 } else if ((c & 0xf8) == 0xf0) {
1149 term->utf_size = term->utf_state = 3;
1150 term->utf_char = (c & 0x07);
1151 } else if ((c & 0xfc) == 0xf8) {
1152 term->utf_size = term->utf_state = 4;
1153 term->utf_char = (c & 0x03);
1154 } else if ((c & 0xfe) == 0xfc) {
1155 term->utf_size = term->utf_state = 5;
1156 term->utf_char = (c & 0x01);
1167 if ((c & 0xC0) != 0x80) {
1170 term->utf_state = 0;
1173 term->utf_char = (term->utf_char << 6) | (c & 0x3f);
1174 if (--term->utf_state)
1179 /* Is somebody trying to be evil! */
1181 (c < 0x800 && term->utf_size >= 2) ||
1182 (c < 0x10000 && term->utf_size >= 3) ||
1183 (c < 0x200000 && term->utf_size >= 4) ||
1184 (c < 0x4000000 && term->utf_size >= 5))
1187 /* Unicode line separator and paragraph separator are CR-LF */
1188 if (c == 0x2028 || c == 0x2029)
1191 /* High controls are probably a Baaad idea too. */
1195 /* The UTF-16 surrogates are not nice either. */
1196 /* The standard give the option of decoding these:
1197 * I don't want to! */
1198 if (c >= 0xD800 && c < 0xE000)
1201 /* ISO 10646 characters now limited to UTF-16 range. */
1205 /* This is currently a TagPhobic application.. */
1206 if (c >= 0xE0000 && c <= 0xE007F)
1209 /* U+FEFF is best seen as a null. */
1212 /* But U+FFFE is an error. */
1213 if (c == 0xFFFE || c == 0xFFFF)
1216 /* Oops this is a 16bit implementation */
1221 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1222 else if(term->sco_acs &&
1223 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1225 if (term->sco_acs == 2) c ^= 0x80;
1228 switch (term->cset_attr[term->cset]) {
1230 * Linedraw characters are different from 'ESC ( B'
1231 * only for a small range. For ones outside that
1232 * range, make sure we use the same font as well as
1233 * the same encoding.
1236 if (unitab_ctrl[c] != 0xFF)
1239 c = ((unsigned char) c) | ATTR_LINEDRW;
1243 /* If UK-ASCII, make the '#' a LineDraw Pound */
1245 c = '}' | ATTR_LINEDRW;
1248 /*FALLTHROUGH*/ case ATTR_ASCII:
1249 if (unitab_ctrl[c] != 0xFF)
1252 c = ((unsigned char) c) | ATTR_ASCII;
1255 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1261 /* How about C1 controls ? */
1262 if ((c & -32) == 0x80 && term->termstate < DO_CTRLS &&
1263 !term->vt52_mode && has_compat(VT220)) {
1264 term->termstate = SEEN_ESC;
1265 term->esc_query = FALSE;
1266 c = '@' + (c & 0x1F);
1269 /* Or the GL control. */
1270 if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) {
1271 if (term->curs.x && !term->wrapnext)
1273 term->wrapnext = FALSE;
1275 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1276 *term->cpos = (' ' | term->curr_attr | ATTR_ASCII);
1278 /* Or normal C0 controls. */
1279 if ((c & -32) == 0 && term->termstate < DO_CTRLS) {
1281 case '\005': /* terminal type query */
1282 /* Strictly speaking this is VT100 but a VT100 defaults to
1283 * no response. Other terminals respond at their option.
1285 * Don't put a CR in the default string as this tends to
1286 * upset some weird software.
1288 * An xterm returns "xterm" (5 characters)
1290 compatibility(ANSIMIN);
1292 char abuf[256], *s, *d;
1294 for (s = cfg.answerback, d = abuf; *s; s++) {
1296 if (*s >= 'a' && *s <= 'z')
1297 *d++ = (*s - ('a' - 1));
1298 else if ((*s >= '@' && *s <= '_') ||
1299 *s == '?' || (*s & 0x80))
1304 } else if (*s == '^') {
1309 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1314 struct beeptime *newbeep;
1315 unsigned long ticks;
1317 ticks = GETTICKCOUNT();
1319 if (!term->beep_overloaded) {
1320 newbeep = smalloc(sizeof(struct beeptime));
1321 newbeep->ticks = ticks;
1322 newbeep->next = NULL;
1323 if (!term->beephead)
1324 term->beephead = newbeep;
1326 term->beeptail->next = newbeep;
1327 term->beeptail = newbeep;
1332 * Throw out any beeps that happened more than
1335 while (term->beephead &&
1336 term->beephead->ticks < ticks - cfg.bellovl_t) {
1337 struct beeptime *tmp = term->beephead;
1338 term->beephead = tmp->next;
1340 if (!term->beephead)
1341 term->beeptail = NULL;
1345 if (cfg.bellovl && term->beep_overloaded &&
1346 ticks - term->lastbeep >= (unsigned)cfg.bellovl_s) {
1348 * If we're currently overloaded and the
1349 * last beep was more than s seconds ago,
1350 * leave overload mode.
1352 term->beep_overloaded = FALSE;
1353 } else if (cfg.bellovl && !term->beep_overloaded &&
1354 term->nbeeps >= cfg.bellovl_n) {
1356 * Now, if we have n or more beeps
1357 * remaining in the queue, go into overload
1360 term->beep_overloaded = TRUE;
1362 term->lastbeep = ticks;
1365 * Perform an actual beep if we're not overloaded.
1367 if (!cfg.bellovl || !term->beep_overloaded) {
1368 if (cfg.beep == BELL_VISUAL) {
1369 term->in_vbell = TRUE;
1370 term->vbell_startpoint = ticks;
1379 if (term->curs.x == 0 &&
1380 (term->curs.y == 0 || term->wrap == 0))
1382 else if (term->curs.x == 0 && term->curs.y > 0)
1383 term->curs.x = term->cols - 1, term->curs.y--;
1384 else if (term->wrapnext)
1385 term->wrapnext = FALSE;
1389 term->seen_disp_event = TRUE;
1392 compatibility(VT100);
1396 compatibility(VT100);
1400 if (term->vt52_mode)
1401 term->termstate = VT52_ESC;
1403 compatibility(ANSIMIN);
1404 term->termstate = SEEN_ESC;
1405 term->esc_query = FALSE;
1410 term->wrapnext = FALSE;
1412 term->seen_disp_event = TRUE;
1413 term->paste_hold = 0;
1414 logtraffic((unsigned char) c, LGTYP_ASCII);
1417 if (has_compat(SCOANSI)) {
1418 move(term, 0, 0, 0);
1419 erase_lots(term, FALSE, FALSE, TRUE);
1421 term->wrapnext = FALSE;
1422 term->seen_disp_event = 1;
1426 compatibility(VT100);
1428 if (term->curs.y == term->marg_b)
1429 scroll(term, term->marg_t, term->marg_b, 1, TRUE);
1430 else if (term->curs.y < term->rows - 1)
1435 term->wrapnext = FALSE;
1436 term->seen_disp_event = 1;
1437 term->paste_hold = 0;
1438 logtraffic((unsigned char) c, LGTYP_ASCII);
1442 pos old_curs = term->curs;
1443 unsigned long *ldata = lineptr(term->curs.y);
1447 } while (term->curs.x < term->cols - 1 &&
1448 !term->tabs[term->curs.x]);
1450 if ((ldata[term->cols] & LATTR_MODE) != LATTR_NORM) {
1451 if (term->curs.x >= term->cols / 2)
1452 term->curs.x = term->cols / 2 - 1;
1454 if (term->curs.x >= term->cols)
1455 term->curs.x = term->cols - 1;
1459 check_selection(term, old_curs, term->curs);
1461 term->seen_disp_event = TRUE;
1465 switch (term->termstate) {
1467 /* Only graphic characters get this far;
1468 * ctrls are stripped above */
1469 if (term->wrapnext && term->wrap) {
1470 term->cpos[1] |= LATTR_WRAPPED;
1471 if (term->curs.y == term->marg_b)
1472 scroll(term, term->marg_t, term->marg_b, 1, TRUE);
1473 else if (term->curs.y < term->rows - 1)
1477 term->wrapnext = FALSE;
1481 if (term->selstate != NO_SELECTION) {
1482 pos cursplus = term->curs;
1484 check_selection(term, term->curs, cursplus);
1486 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1487 logtraffic((unsigned char) c, LGTYP_ASCII);
1489 extern int wcwidth(wchar_t ucs);
1494 width = wcwidth((wchar_t) c);
1497 *term->cpos++ = c | term->curr_attr;
1498 if (++term->curs.x == term->cols) {
1499 *term->cpos |= LATTR_WRAPPED;
1500 if (term->curs.y == term->marg_b)
1501 scroll(term, term->marg_t, term->marg_b,
1503 else if (term->curs.y < term->rows - 1)
1508 *term->cpos++ = UCSWIDE | term->curr_attr;
1511 *term->cpos++ = c | term->curr_attr;
1518 if (term->curs.x == term->cols) {
1521 term->wrapnext = TRUE;
1522 if (term->wrap && term->vt52_mode) {
1523 term->cpos[1] |= LATTR_WRAPPED;
1524 if (term->curs.y == term->marg_b)
1525 scroll(term, term->marg_t, term->marg_b, 1, TRUE);
1526 else if (term->curs.y < term->rows - 1)
1530 term->wrapnext = FALSE;
1533 term->seen_disp_event = 1;
1538 * This state is virtually identical to SEEN_ESC, with the
1539 * exception that we have an OSC sequence in the pipeline,
1540 * and _if_ we see a backslash, we process it.
1544 term->termstate = TOPLEVEL;
1547 /* else fall through */
1549 if (c >= ' ' && c <= '/') {
1550 if (term->esc_query)
1551 term->esc_query = -1;
1553 term->esc_query = c;
1556 term->termstate = TOPLEVEL;
1557 switch (ANSI(c, term->esc_query)) {
1558 case '[': /* enter CSI mode */
1559 term->termstate = SEEN_CSI;
1560 term->esc_nargs = 1;
1561 term->esc_args[0] = ARG_DEFAULT;
1562 term->esc_query = FALSE;
1564 case ']': /* xterm escape sequences */
1565 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1566 compatibility(OTHER);
1567 term->termstate = SEEN_OSC;
1568 term->esc_args[0] = 0;
1570 case '7': /* save cursor */
1571 compatibility(VT100);
1572 save_cursor(term, TRUE);
1574 case '8': /* restore cursor */
1575 compatibility(VT100);
1576 save_cursor(term, FALSE);
1577 term->seen_disp_event = TRUE;
1580 compatibility(VT100);
1581 term->app_keypad_keys = TRUE;
1584 compatibility(VT100);
1585 term->app_keypad_keys = FALSE;
1587 case 'D': /* exactly equivalent to LF */
1588 compatibility(VT100);
1589 if (term->curs.y == term->marg_b)
1590 scroll(term, term->marg_t, term->marg_b, 1, TRUE);
1591 else if (term->curs.y < term->rows - 1)
1594 term->wrapnext = FALSE;
1595 term->seen_disp_event = TRUE;
1597 case 'E': /* exactly equivalent to CR-LF */
1598 compatibility(VT100);
1600 if (term->curs.y == term->marg_b)
1601 scroll(term, term->marg_t, term->marg_b, 1, TRUE);
1602 else if (term->curs.y < term->rows - 1)
1605 term->wrapnext = FALSE;
1606 term->seen_disp_event = TRUE;
1608 case 'M': /* reverse index - backwards LF */
1609 compatibility(VT100);
1610 if (term->curs.y == term->marg_t)
1611 scroll(term, term->marg_t, term->marg_b, -1, TRUE);
1612 else if (term->curs.y > 0)
1615 term->wrapnext = FALSE;
1616 term->seen_disp_event = TRUE;
1618 case 'Z': /* terminal type query */
1619 compatibility(VT100);
1620 ldisc_send(term->id_string, strlen(term->id_string), 0);
1622 case 'c': /* restore power-on settings */
1623 compatibility(VT100);
1625 ldisc_send(NULL, 0, 0);/* cause ldisc to notice changes */
1626 if (term->reset_132) {
1627 if (!cfg.no_remote_resize)
1628 request_resize(80, term->rows);
1629 term->reset_132 = 0;
1633 term->seen_disp_event = TRUE;
1635 case 'H': /* set a tab */
1636 compatibility(VT100);
1637 term->tabs[term->curs.x] = TRUE;
1640 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1641 compatibility(VT100);
1643 unsigned long *ldata;
1647 for (i = 0; i < term->rows; i++) {
1649 for (j = 0; j < term->cols; j++)
1650 ldata[j] = ATTR_DEFAULT | 'E';
1651 ldata[term->cols] = 0;
1654 term->seen_disp_event = TRUE;
1655 scrtop.x = scrtop.y = 0;
1657 scrbot.y = term->rows;
1658 check_selection(term, scrtop, scrbot);
1662 case ANSI('3', '#'):
1663 case ANSI('4', '#'):
1664 case ANSI('5', '#'):
1665 case ANSI('6', '#'):
1666 compatibility(VT100);
1668 unsigned long nlattr;
1669 unsigned long *ldata;
1670 switch (ANSI(c, term->esc_query)) {
1671 case ANSI('3', '#'):
1674 case ANSI('4', '#'):
1677 case ANSI('5', '#'):
1678 nlattr = LATTR_NORM;
1680 default: /* spiritually case ANSI('6', '#'): */
1681 nlattr = LATTR_WIDE;
1684 ldata = lineptr(term->curs.y);
1685 ldata[term->cols] &= ~LATTR_MODE;
1686 ldata[term->cols] |= nlattr;
1690 case ANSI('A', '('):
1691 compatibility(VT100);
1692 if (!cfg.no_remote_charset)
1693 term->cset_attr[0] = ATTR_GBCHR;
1695 case ANSI('B', '('):
1696 compatibility(VT100);
1697 if (!cfg.no_remote_charset)
1698 term->cset_attr[0] = ATTR_ASCII;
1700 case ANSI('0', '('):
1701 compatibility(VT100);
1702 if (!cfg.no_remote_charset)
1703 term->cset_attr[0] = ATTR_LINEDRW;
1705 case ANSI('U', '('):
1706 compatibility(OTHER);
1707 if (!cfg.no_remote_charset)
1708 term->cset_attr[0] = ATTR_SCOACS;
1711 case ANSI('A', ')'):
1712 compatibility(VT100);
1713 if (!cfg.no_remote_charset)
1714 term->cset_attr[1] = ATTR_GBCHR;
1716 case ANSI('B', ')'):
1717 compatibility(VT100);
1718 if (!cfg.no_remote_charset)
1719 term->cset_attr[1] = ATTR_ASCII;
1721 case ANSI('0', ')'):
1722 compatibility(VT100);
1723 if (!cfg.no_remote_charset)
1724 term->cset_attr[1] = ATTR_LINEDRW;
1726 case ANSI('U', ')'):
1727 compatibility(OTHER);
1728 if (!cfg.no_remote_charset)
1729 term->cset_attr[1] = ATTR_SCOACS;
1732 case ANSI('8', '%'): /* Old Linux code */
1733 case ANSI('G', '%'):
1734 compatibility(OTHER);
1735 if (!cfg.no_remote_charset)
1738 case ANSI('@', '%'):
1739 compatibility(OTHER);
1740 if (!cfg.no_remote_charset)
1746 term->termstate = TOPLEVEL; /* default */
1748 if (term->esc_nargs <= ARGS_MAX) {
1749 if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)
1750 term->esc_args[term->esc_nargs - 1] = 0;
1751 term->esc_args[term->esc_nargs - 1] =
1752 10 * term->esc_args[term->esc_nargs - 1] + c - '0';
1754 term->termstate = SEEN_CSI;
1755 } else if (c == ';') {
1756 if (++term->esc_nargs <= ARGS_MAX)
1757 term->esc_args[term->esc_nargs - 1] = ARG_DEFAULT;
1758 term->termstate = SEEN_CSI;
1759 } else if (c < '@') {
1760 if (term->esc_query)
1761 term->esc_query = -1;
1763 term->esc_query = TRUE;
1765 term->esc_query = c;
1766 term->termstate = SEEN_CSI;
1768 switch (ANSI(c, term->esc_query)) {
1769 case 'A': /* move up N lines */
1770 move(term, term->curs.x,
1771 term->curs.y - def(term->esc_args[0], 1), 1);
1772 term->seen_disp_event = TRUE;
1774 case 'e': /* move down N lines */
1775 compatibility(ANSI);
1778 move(term, term->curs.x,
1779 term->curs.y + def(term->esc_args[0], 1), 1);
1780 term->seen_disp_event = TRUE;
1782 case ANSI('c', '>'): /* report xterm version */
1783 compatibility(OTHER);
1784 /* this reports xterm version 136 so that VIM can
1785 use the drag messages from the mouse reporting */
1786 ldisc_send("\033[>0;136;0c", 11, 0);
1788 case 'a': /* move right N cols */
1789 compatibility(ANSI);
1792 move(term, term->curs.x + def(term->esc_args[0], 1),
1794 term->seen_disp_event = TRUE;
1796 case 'D': /* move left N cols */
1797 move(term, term->curs.x - def(term->esc_args[0], 1),
1799 term->seen_disp_event = TRUE;
1801 case 'E': /* move down N lines and CR */
1802 compatibility(ANSI);
1804 term->curs.y + def(term->esc_args[0], 1), 1);
1805 term->seen_disp_event = TRUE;
1807 case 'F': /* move up N lines and CR */
1808 compatibility(ANSI);
1810 term->curs.y - def(term->esc_args[0], 1), 1);
1811 term->seen_disp_event = TRUE;
1814 case '`': /* set horizontal posn */
1815 compatibility(ANSI);
1816 move(term, def(term->esc_args[0], 1) - 1,
1818 term->seen_disp_event = TRUE;
1820 case 'd': /* set vertical posn */
1821 compatibility(ANSI);
1822 move(term, term->curs.x,
1823 ((term->dec_om ? term->marg_t : 0) +
1824 def(term->esc_args[0], 1) - 1),
1825 (term->dec_om ? 2 : 0));
1826 term->seen_disp_event = TRUE;
1829 case 'f': /* set horz and vert posns at once */
1830 if (term->esc_nargs < 2)
1831 term->esc_args[1] = ARG_DEFAULT;
1832 move(term, def(term->esc_args[1], 1) - 1,
1833 ((term->dec_om ? term->marg_t : 0) +
1834 def(term->esc_args[0], 1) - 1),
1835 (term->dec_om ? 2 : 0));
1836 term->seen_disp_event = TRUE;
1838 case 'J': /* erase screen or parts of it */
1840 unsigned int i = def(term->esc_args[0], 0) + 1;
1843 erase_lots(term, FALSE, !!(i & 2), !!(i & 1));
1846 term->seen_disp_event = TRUE;
1848 case 'K': /* erase line or parts of it */
1850 unsigned int i = def(term->esc_args[0], 0) + 1;
1853 erase_lots(term, TRUE, !!(i & 2), !!(i & 1));
1855 term->seen_disp_event = TRUE;
1857 case 'L': /* insert lines */
1858 compatibility(VT102);
1859 if (term->curs.y <= term->marg_b)
1860 scroll(term, term->curs.y, term->marg_b,
1861 -def(term->esc_args[0], 1), FALSE);
1863 term->seen_disp_event = TRUE;
1865 case 'M': /* delete lines */
1866 compatibility(VT102);
1867 if (term->curs.y <= term->marg_b)
1868 scroll(term, term->curs.y, term->marg_b,
1869 def(term->esc_args[0], 1),
1872 term->seen_disp_event = TRUE;
1874 case '@': /* insert chars */
1875 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1876 compatibility(VT102);
1877 insch(term, def(term->esc_args[0], 1));
1878 term->seen_disp_event = TRUE;
1880 case 'P': /* delete chars */
1881 compatibility(VT102);
1882 insch(term, -def(term->esc_args[0], 1));
1883 term->seen_disp_event = TRUE;
1885 case 'c': /* terminal type query */
1886 compatibility(VT100);
1887 /* This is the response for a VT102 */
1888 ldisc_send(term->id_string,
1889 strlen(term->id_string), 0);
1891 case 'n': /* cursor position query */
1892 if (term->esc_args[0] == 6) {
1894 sprintf(buf, "\033[%d;%dR", term->curs.y + 1,
1896 ldisc_send(buf, strlen(buf), 0);
1897 } else if (term->esc_args[0] == 5) {
1898 ldisc_send("\033[0n", 4, 0);
1901 case 'h': /* toggle modes to high */
1903 compatibility(VT100);
1906 for (i = 0; i < term->esc_nargs; i++)
1907 toggle_mode(term, term->esc_args[i],
1908 term->esc_query, TRUE);
1913 compatibility(VT100);
1915 if (term->esc_nargs != 1) break;
1916 if (term->esc_args[0] == 5 && *cfg.printer) {
1917 term->printing = TRUE;
1918 term->only_printing = !term->esc_query;
1919 term->print_state = 0;
1920 term_print_setup(term);
1921 } else if (term->esc_args[0] == 4 &&
1923 term_print_finish(term);
1927 case 'l': /* toggle modes to low */
1929 compatibility(VT100);
1932 for (i = 0; i < term->esc_nargs; i++)
1933 toggle_mode(term, term->esc_args[i],
1934 term->esc_query, FALSE);
1937 case 'g': /* clear tabs */
1938 compatibility(VT100);
1939 if (term->esc_nargs == 1) {
1940 if (term->esc_args[0] == 0) {
1941 term->tabs[term->curs.x] = FALSE;
1942 } else if (term->esc_args[0] == 3) {
1944 for (i = 0; i < term->cols; i++)
1945 term->tabs[i] = FALSE;
1949 case 'r': /* set scroll margins */
1950 compatibility(VT100);
1951 if (term->esc_nargs <= 2) {
1953 top = def(term->esc_args[0], 1) - 1;
1954 bot = (term->esc_nargs <= 1
1955 || term->esc_args[1] == 0 ?
1957 def(term->esc_args[1], term->rows)) - 1;
1958 if (bot >= term->rows)
1959 bot = term->rows - 1;
1960 /* VTTEST Bug 9 - if region is less than 2 lines
1961 * don't change region.
1963 if (bot - top > 0) {
1968 * I used to think the cursor should be
1969 * placed at the top of the newly marginned
1970 * area. Apparently not: VMS TPU falls over
1973 * Well actually it should for
1976 term->curs.y = (term->dec_om ?
1979 term->seen_disp_event = TRUE;
1983 case 'm': /* set graphics rendition */
1986 * A VT100 without the AVO only had one
1987 * attribute, either underline or
1988 * reverse video depending on the
1989 * cursor type, this was selected by
1993 * This is sometimes DIM, eg on the
1996 * This is sometimes INVIS various ANSI.
1998 * This like 22 disables BOLD, DIM and INVIS
2000 * The ANSI colours appear on any
2001 * terminal that has colour (obviously)
2002 * but the interaction between sgr0 and
2003 * the colours varies but is usually
2004 * related to the background colour
2005 * erase item. The interaction between
2006 * colour attributes and the mono ones
2007 * is also very implementation
2010 * The 39 and 49 attributes are likely
2011 * to be unimplemented.
2014 for (i = 0; i < term->esc_nargs; i++) {
2015 switch (def(term->esc_args[i], 0)) {
2016 case 0: /* restore defaults */
2017 term->curr_attr = ATTR_DEFAULT;
2019 case 1: /* enable bold */
2020 compatibility(VT100AVO);
2021 term->curr_attr |= ATTR_BOLD;
2023 case 21: /* (enable double underline) */
2024 compatibility(OTHER);
2025 case 4: /* enable underline */
2026 compatibility(VT100AVO);
2027 term->curr_attr |= ATTR_UNDER;
2029 case 5: /* enable blink */
2030 compatibility(VT100AVO);
2031 term->curr_attr |= ATTR_BLINK;
2033 case 7: /* enable reverse video */
2034 term->curr_attr |= ATTR_REVERSE;
2036 case 10: /* SCO acs off */
2037 compatibility(SCOANSI);
2038 if (cfg.no_remote_charset) break;
2039 term->sco_acs = 0; break;
2040 case 11: /* SCO acs on */
2041 compatibility(SCOANSI);
2042 if (cfg.no_remote_charset) break;
2043 term->sco_acs = 1; break;
2044 case 12: /* SCO acs on flipped */
2045 compatibility(SCOANSI);
2046 if (cfg.no_remote_charset) break;
2047 term->sco_acs = 2; break;
2048 case 22: /* disable bold */
2049 compatibility2(OTHER, VT220);
2050 term->curr_attr &= ~ATTR_BOLD;
2052 case 24: /* disable underline */
2053 compatibility2(OTHER, VT220);
2054 term->curr_attr &= ~ATTR_UNDER;
2056 case 25: /* disable blink */
2057 compatibility2(OTHER, VT220);
2058 term->curr_attr &= ~ATTR_BLINK;
2060 case 27: /* disable reverse video */
2061 compatibility2(OTHER, VT220);
2062 term->curr_attr &= ~ATTR_REVERSE;
2073 term->curr_attr &= ~ATTR_FGMASK;
2075 (term->esc_args[i] - 30)<<ATTR_FGSHIFT;
2077 case 39: /* default-foreground */
2078 term->curr_attr &= ~ATTR_FGMASK;
2079 term->curr_attr |= ATTR_DEFFG;
2090 term->curr_attr &= ~ATTR_BGMASK;
2092 (term->esc_args[i] - 40)<<ATTR_BGSHIFT;
2094 case 49: /* default-background */
2095 term->curr_attr &= ~ATTR_BGMASK;
2096 term->curr_attr |= ATTR_DEFBG;
2101 term->erase_char = (' ' | ATTR_ASCII |
2107 case 's': /* save cursor */
2108 save_cursor(term, TRUE);
2110 case 'u': /* restore cursor */
2111 save_cursor(term, FALSE);
2112 term->seen_disp_event = TRUE;
2114 case 't': /* set page size - ie window height */
2116 * VT340/VT420 sequence DECSLPP, DEC only allows values
2117 * 24/25/36/48/72/144 other emulators (eg dtterm) use
2118 * illegal values (eg first arg 1..9) for window changing
2121 if (term->esc_nargs <= 1
2122 && (term->esc_args[0] < 1 ||
2123 term->esc_args[0] >= 24)) {
2124 compatibility(VT340TEXT);
2125 if (!cfg.no_remote_resize)
2126 request_resize(term->cols,
2127 def(term->esc_args[0], 24));
2129 } else if (term->esc_nargs >= 1 &&
2130 term->esc_args[0] >= 1 &&
2131 term->esc_args[0] < 24) {
2132 compatibility(OTHER);
2134 switch (term->esc_args[0]) {
2144 if (term->esc_nargs >= 3) {
2145 if (!cfg.no_remote_resize)
2146 move_window(def(term->esc_args[1], 0),
2147 def(term->esc_args[2], 0));
2151 /* We should resize the window to a given
2152 * size in pixels here, but currently our
2153 * resizing code isn't healthy enough to
2157 set_zorder(TRUE); /* move to top */
2160 set_zorder(FALSE); /* move to bottom */
2166 if (term->esc_nargs >= 3) {
2167 if (!cfg.no_remote_resize)
2168 request_resize(def(term->esc_args[2], cfg.width),
2169 def(term->esc_args[1], cfg.height));
2173 if (term->esc_nargs >= 2)
2174 set_zoomed(term->esc_args[1] ?
2178 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2182 get_window_pos(&x, &y);
2183 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2184 ldisc_send(buf, len, 0);
2187 get_window_pixels(&x, &y);
2188 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2189 ldisc_send(buf, len, 0);
2192 len = sprintf(buf, "\033[8;%d;%dt",
2193 term->rows, term->cols);
2194 ldisc_send(buf, len, 0);
2198 * Hmmm. Strictly speaking we
2199 * should return `the size of the
2200 * screen in characters', but
2201 * that's not easy: (a) window
2202 * furniture being what it is it's
2203 * hard to compute, and (b) in
2204 * resize-font mode maximising the
2205 * window wouldn't change the
2206 * number of characters. *shrug*. I
2207 * think we'll ignore it for the
2208 * moment and see if anyone
2209 * complains, and then ask them
2210 * what they would like it to do.
2214 p = get_window_title(TRUE);
2216 ldisc_send("\033]L", 3, 0);
2217 ldisc_send(p, len, 0);
2218 ldisc_send("\033\\", 2, 0);
2221 p = get_window_title(FALSE);
2223 ldisc_send("\033]l", 3, 0);
2224 ldisc_send(p, len, 0);
2225 ldisc_send("\033\\", 2, 0);
2231 compatibility(SCOANSI);
2232 scroll(term, term->marg_t, term->marg_b,
2233 def(term->esc_args[0], 1), TRUE);
2235 term->wrapnext = FALSE;
2236 term->seen_disp_event = TRUE;
2239 compatibility(SCOANSI);
2240 scroll(term, term->marg_t, term->marg_b,
2241 -def(term->esc_args[0], 1), TRUE);
2243 term->wrapnext = FALSE;
2244 term->seen_disp_event = TRUE;
2246 case ANSI('|', '*'):
2247 /* VT420 sequence DECSNLS
2248 * Set number of lines on screen
2249 * VT420 uses VGA like hardware and can support any size in
2250 * reasonable range (24..49 AIUI) with no default specified.
2252 compatibility(VT420);
2253 if (term->esc_nargs == 1 && term->esc_args[0] > 0) {
2254 if (!cfg.no_remote_resize)
2255 request_resize(term->cols,
2256 def(term->esc_args[0],
2261 case ANSI('|', '$'):
2262 /* VT340/VT420 sequence DECSCPP
2263 * Set number of columns per page
2264 * Docs imply range is only 80 or 132, but I'll allow any.
2266 compatibility(VT340TEXT);
2267 if (term->esc_nargs <= 1) {
2268 if (!cfg.no_remote_resize)
2269 request_resize(def(term->esc_args[0],
2270 cfg.width), term->rows);
2274 case 'X': /* write N spaces w/o moving cursor */
2275 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2276 compatibility(ANSIMIN);
2278 int n = def(term->esc_args[0], 1);
2280 unsigned long *p = term->cpos;
2281 if (n > term->cols - term->curs.x)
2282 n = term->cols - term->curs.x;
2283 cursplus = term->curs;
2285 check_selection(term, term->curs, cursplus);
2287 *p++ = term->erase_char;
2288 term->seen_disp_event = TRUE;
2291 case 'x': /* report terminal characteristics */
2292 compatibility(VT100);
2295 int i = def(term->esc_args[0], 0);
2296 if (i == 0 || i == 1) {
2297 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2299 ldisc_send(buf, 20, 0);
2303 case 'Z': /* BackTab for xterm */
2304 compatibility(OTHER);
2306 int i = def(term->esc_args[0], 1);
2307 pos old_curs = term->curs;
2309 for(;i>0 && term->curs.x>0; i--) {
2312 } while (term->curs.x >0 &&
2313 !term->tabs[term->curs.x]);
2316 check_selection(term, old_curs, term->curs);
2319 case ANSI('L', '='):
2320 compatibility(OTHER);
2321 term->use_bce = (term->esc_args[0] <= 0);
2322 term->erase_char = ERASE_CHAR;
2324 term->erase_char = (' ' | ATTR_ASCII |
2326 (ATTR_FGMASK | ATTR_BGMASK)));
2328 case ANSI('E', '='):
2329 compatibility(OTHER);
2330 term->blink_is_real = (term->esc_args[0] >= 1);
2332 case ANSI('p', '"'):
2334 * Allow the host to make this emulator a
2335 * 'perfect' VT102. This first appeared in
2336 * the VT220, but we do need to get back to
2337 * PuTTY mode so I won't check it.
2339 * The arg in 40..42,50 are a PuTTY extension.
2340 * The 2nd arg, 8bit vs 7bit is not checked.
2342 * Setting VT102 mode should also change
2343 * the Fkeys to generate PF* codes as a
2344 * real VT102 has no Fkeys. The VT220 does
2345 * this, F11..F13 become ESC,BS,LF other
2346 * Fkeys send nothing.
2348 * Note ESC c will NOT change this!
2351 switch (term->esc_args[0]) {
2353 term->compatibility_level &= ~TM_VTXXX;
2354 term->compatibility_level |= TM_VT102;
2357 term->compatibility_level &= ~TM_VTXXX;
2358 term->compatibility_level |= TM_VT220;
2362 if (term->esc_args[0] > 60 &&
2363 term->esc_args[0] < 70)
2364 term->compatibility_level |= TM_VTXXX;
2368 term->compatibility_level &= TM_VTXXX;
2371 term->compatibility_level = TM_PUTTY;
2374 term->compatibility_level = TM_SCOANSI;
2378 term->compatibility_level = TM_PUTTY;
2384 /* Change the response to CSI c */
2385 if (term->esc_args[0] == 50) {
2388 strcpy(term->id_string, "\033[?");
2389 for (i = 1; i < term->esc_nargs; i++) {
2391 strcat(term->id_string, ";");
2392 sprintf(lbuf, "%d", term->esc_args[i]);
2393 strcat(term->id_string, lbuf);
2395 strcat(term->id_string, "c");
2398 /* Is this a good idea ?
2399 * Well we should do a soft reset at this point ...
2401 if (!has_compat(VT420) && has_compat(VT100)) {
2402 if (!cfg.no_remote_resize) {
2403 if (term->reset_132)
2404 request_resize(132, 24);
2406 request_resize(80, 24);
2414 term->osc_w = FALSE;
2416 case 'P': /* Linux palette sequence */
2417 term->termstate = SEEN_OSC_P;
2418 term->osc_strlen = 0;
2420 case 'R': /* Linux palette reset */
2422 term_invalidate(term);
2423 term->termstate = TOPLEVEL;
2425 case 'W': /* word-set */
2426 term->termstate = SEEN_OSC_W;
2439 term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
2443 * Grotty hack to support xterm and DECterm title
2444 * sequences concurrently.
2446 if (term->esc_args[0] == 2) {
2447 term->esc_args[0] = 1;
2450 /* else fall through */
2452 term->termstate = OSC_STRING;
2453 term->osc_strlen = 0;
2458 * This OSC stuff is EVIL. It takes just one character to get into
2459 * sysline mode and it's not initially obvious how to get out.
2460 * So I've added CR and LF as string aborts.
2461 * This shouldn't effect compatibility as I believe embedded
2462 * control characters are supposed to be interpreted (maybe?)
2463 * and they don't display anything useful anyway.
2467 if (c == '\n' || c == '\r') {
2468 term->termstate = TOPLEVEL;
2469 } else if (c == 0234 || c == '\007') {
2471 * These characters terminate the string; ST and BEL
2472 * terminate the sequence and trigger instant
2473 * processing of it, whereas ESC goes back to SEEN_ESC
2474 * mode unless it is followed by \, in which case it is
2475 * synonymous with ST in the first place.
2478 term->termstate = TOPLEVEL;
2479 } else if (c == '\033')
2480 term->termstate = OSC_MAYBE_ST;
2481 else if (term->osc_strlen < OSC_STR_MAX)
2482 term->osc_string[term->osc_strlen++] = c;
2486 int max = (term->osc_strlen == 0 ? 21 : 16);
2488 if (c >= '0' && c <= '9')
2490 else if (c >= 'A' && c <= 'A' + max - 10)
2492 else if (c >= 'a' && c <= 'a' + max - 10)
2495 term->termstate = TOPLEVEL;
2498 term->osc_string[term->osc_strlen++] = val;
2499 if (term->osc_strlen >= 7) {
2500 palette_set(term->osc_string[0],
2501 term->osc_string[1] * 16 + term->osc_string[2],
2502 term->osc_string[3] * 16 + term->osc_string[4],
2503 term->osc_string[5] * 16 + term->osc_string[6]);
2504 term_invalidate(term);
2505 term->termstate = TOPLEVEL;
2521 term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
2524 term->termstate = OSC_STRING;
2525 term->osc_strlen = 0;
2529 term->termstate = TOPLEVEL;
2530 term->seen_disp_event = TRUE;
2533 move(term, term->curs.x, term->curs.y - 1, 1);
2536 move(term, term->curs.x, term->curs.y + 1, 1);
2539 move(term, term->curs.x + 1, term->curs.y, 1);
2542 move(term, term->curs.x - 1, term->curs.y, 1);
2545 * From the VT100 Manual
2546 * NOTE: The special graphics characters in the VT100
2547 * are different from those in the VT52
2549 * From VT102 manual:
2550 * 137 _ Blank - Same
2551 * 140 ` Reserved - Humm.
2552 * 141 a Solid rectangle - Similar
2553 * 142 b 1/ - Top half of fraction for the
2554 * 143 c 3/ - subscript numbers below.
2557 * 146 f Degrees - Same
2558 * 147 g Plus or minus - Same
2560 * 151 i Ellipsis (dots)
2563 * 154 l Bar at scan 0
2564 * 155 m Bar at scan 1
2565 * 156 n Bar at scan 2
2566 * 157 o Bar at scan 3 - Similar
2567 * 160 p Bar at scan 4 - Similar
2568 * 161 q Bar at scan 5 - Similar
2569 * 162 r Bar at scan 6 - Same
2570 * 163 s Bar at scan 7 - Similar
2585 term->cset_attr[term->cset = 0] = ATTR_LINEDRW;
2588 term->cset_attr[term->cset = 0] = ATTR_ASCII;
2591 move(term, 0, 0, 0);
2594 if (term->curs.y == 0)
2595 scroll(term, 0, term->rows - 1, -1, TRUE);
2596 else if (term->curs.y > 0)
2599 term->wrapnext = FALSE;
2602 erase_lots(term, FALSE, FALSE, TRUE);
2606 erase_lots(term, TRUE, FALSE, TRUE);
2610 /* XXX Print cursor line */
2613 /* XXX Start controller mode */
2616 /* XXX Stop controller mode */
2620 term->termstate = VT52_Y1;
2623 ldisc_send("\033/Z", 3, 0);
2626 term->app_keypad_keys = TRUE;
2629 term->app_keypad_keys = FALSE;
2632 /* XXX This should switch to VT100 mode not current or default
2633 * VT mode. But this will only have effect in a VT220+
2636 term->vt52_mode = FALSE;
2637 term->blink_is_real = cfg.blinktext;
2641 /* XXX Enter auto print mode */
2644 /* XXX Exit auto print mode */
2647 /* XXX Print screen */
2653 /* compatibility(ATARI) */
2654 move(term, 0, 0, 0);
2655 erase_lots(term, FALSE, FALSE, TRUE);
2659 /* compatibility(ATARI) */
2660 if (term->curs.y <= term->marg_b)
2661 scroll(term, term->curs.y, term->marg_b, -1, FALSE);
2664 /* compatibility(ATARI) */
2665 if (term->curs.y <= term->marg_b)
2666 scroll(term, term->curs.y, term->marg_b, 1, TRUE);
2669 /* compatibility(ATARI) */
2670 term->termstate = VT52_FG;
2673 /* compatibility(ATARI) */
2674 term->termstate = VT52_BG;
2677 /* compatibility(ATARI) */
2678 erase_lots(term, FALSE, TRUE, FALSE);
2682 /* compatibility(ATARI) */
2683 term->cursor_on = TRUE;
2686 /* compatibility(ATARI) */
2687 term->cursor_on = FALSE;
2689 /* case 'j': Save cursor position - broken on ST */
2690 /* case 'k': Restore cursor position */
2692 /* compatibility(ATARI) */
2693 erase_lots(term, TRUE, TRUE, TRUE);
2695 term->wrapnext = FALSE;
2699 /* compatibility(ATARI) */
2700 erase_lots(term, TRUE, TRUE, FALSE);
2703 /* compatibility(ATARI) */
2704 term->curr_attr |= ATTR_REVERSE;
2707 /* compatibility(ATARI) */
2708 term->curr_attr &= ~ATTR_REVERSE;
2710 case 'v': /* wrap Autowrap on - Wyse style */
2711 /* compatibility(ATARI) */
2714 case 'w': /* Autowrap off */
2715 /* compatibility(ATARI) */
2720 /* compatibility(OTHER) */
2721 term->vt52_bold = FALSE;
2722 term->curr_attr = ATTR_DEFAULT;
2724 term->erase_char = (' ' | ATTR_ASCII |
2726 (ATTR_FGMASK | ATTR_BGMASK)));
2729 /* compatibility(VI50) */
2730 term->curr_attr |= ATTR_UNDER;
2733 /* compatibility(VI50) */
2734 term->curr_attr &= ~ATTR_UNDER;
2737 /* compatibility(VI50) */
2738 term->vt52_bold = TRUE;
2739 term->curr_attr |= ATTR_BOLD;
2742 /* compatibility(VI50) */
2743 term->vt52_bold = FALSE;
2744 term->curr_attr &= ~ATTR_BOLD;
2750 term->termstate = VT52_Y2;
2751 move(term, term->curs.x, c - ' ', 0);
2754 term->termstate = TOPLEVEL;
2755 move(term, c - ' ', term->curs.y, 0);
2760 term->termstate = TOPLEVEL;
2761 term->curr_attr &= ~ATTR_FGMASK;
2762 term->curr_attr &= ~ATTR_BOLD;
2763 term->curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2764 if ((c & 0x8) || term->vt52_bold)
2765 term->curr_attr |= ATTR_BOLD;
2768 term->erase_char = (' ' | ATTR_ASCII |
2770 (ATTR_FGMASK | ATTR_BGMASK)));
2773 term->termstate = TOPLEVEL;
2774 term->curr_attr &= ~ATTR_BGMASK;
2775 term->curr_attr &= ~ATTR_BLINK;
2776 term->curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2778 /* Note: bold background */
2780 term->curr_attr |= ATTR_BLINK;
2783 term->erase_char = (' ' | ATTR_ASCII |
2785 (ATTR_FGMASK | ATTR_BGMASK)));
2788 default: break; /* placate gcc warning about enum use */
2790 if (term->selstate != NO_SELECTION) {
2791 pos cursplus = term->curs;
2793 check_selection(term, term->curs, cursplus);
2797 term_print_flush(term);
2802 * Compare two lines to determine whether they are sufficiently
2803 * alike to scroll-optimise one to the other. Return the degree of
2806 static int linecmp(Terminal *term, unsigned long *a, unsigned long *b)
2810 for (i = n = 0; i < term->cols; i++)
2811 n += (*a++ == *b++);
2817 * Given a context, update the window. Out of paranoia, we don't
2818 * allow WM_PAINT responses to do scrolling optimisations.
2820 static void do_paint(Terminal *term, Context ctx, int may_optimise)
2822 int i, j, our_curs_y;
2823 unsigned long rv, cursor;
2826 long cursor_background = ERASE_CHAR;
2827 unsigned long ticks;
2830 * Check the visual bell state.
2832 if (term->in_vbell) {
2833 ticks = GETTICKCOUNT();
2834 if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
2835 term->in_vbell = FALSE;
2838 rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
2841 * screen array, disptop, scrtop,
2843 * cfg.blinkpc, blink_is_real, tblinker,
2844 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2847 /* Has the cursor position or type changed ? */
2848 if (term->cursor_on) {
2849 if (term->has_focus) {
2850 if (term->blinker || !cfg.blink_cur)
2851 cursor = TATTR_ACTCURS;
2855 cursor = TATTR_PASCURS;
2857 cursor |= TATTR_RIGHTCURS;
2860 our_curs_y = term->curs.y - term->disptop;
2862 if (term->dispcurs && (term->curstype != cursor ||
2864 term->disptext + our_curs_y * (term->cols + 1) +
2866 if (term->dispcurs > term->disptext &&
2867 (*term->dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2868 term->dispcurs[-1] |= ATTR_INVALID;
2869 if ( (term->dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2870 term->dispcurs[1] |= ATTR_INVALID;
2871 *term->dispcurs |= ATTR_INVALID;
2874 term->dispcurs = NULL;
2876 /* The normal screen data */
2877 for (i = 0; i < term->rows; i++) {
2878 unsigned long *ldata;
2880 int idx, dirty_line, dirty_run, selected;
2881 unsigned long attr = 0;
2882 int updated_line = 0;
2885 int last_run_dirty = 0;
2887 scrpos.y = i + term->disptop;
2888 ldata = lineptr(scrpos.y);
2889 lattr = (ldata[term->cols] & LATTR_MODE);
2891 idx = i * (term->cols + 1);
2892 dirty_run = dirty_line = (ldata[term->cols] !=
2893 term->disptext[idx + term->cols]);
2894 term->disptext[idx + term->cols] = ldata[term->cols];
2896 for (j = 0; j < term->cols; j++, idx++) {
2897 unsigned long tattr, tchar;
2898 unsigned long *d = ldata + j;
2902 tchar = (*d & (CHAR_MASK | CSET_MASK));
2903 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2904 switch (tchar & CSET_MASK) {
2906 tchar = unitab_line[tchar & 0xFF];
2909 tchar = unitab_xterm[tchar & 0xFF];
2912 tchar = unitab_scoacs[tchar&0xFF];
2915 tattr |= (tchar & CSET_MASK);
2917 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2920 /* Video reversing things */
2921 if (term->selstate == DRAGGING || term->selstate == SELECTED) {
2922 if (term->seltype == LEXICOGRAPHIC)
2923 selected = (posle(term->selstart, scrpos) &&
2924 poslt(scrpos, term->selend));
2926 selected = (posPle(term->selstart, scrpos) &&
2927 posPlt(scrpos, term->selend));
2931 ^ (selected ? ATTR_REVERSE : 0));
2933 /* 'Real' blinking ? */
2934 if (term->blink_is_real && (tattr & ATTR_BLINK)) {
2935 if (term->has_focus && term->tblinker) {
2937 tattr &= ~CSET_MASK;
2940 tattr &= ~ATTR_BLINK;
2944 * Check the font we'll _probably_ be using to see if
2945 * the character is wide when we don't want it to be.
2947 if ((tchar | tattr) != (term->disptext[idx]& ~ATTR_NARROW)) {
2948 if ((tattr & ATTR_WIDE) == 0 &&
2949 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2950 tattr |= ATTR_NARROW;
2951 } else if (term->disptext[idx]&ATTR_NARROW)
2952 tattr |= ATTR_NARROW;
2954 /* Cursor here ? Save the 'background' */
2955 if (i == our_curs_y && j == term->curs.x) {
2956 cursor_background = tattr | tchar;
2957 term->dispcurs = term->disptext + idx;
2960 if ((term->disptext[idx] ^ tattr) & ATTR_WIDE)
2963 break_run = (tattr != attr || j - start >= sizeof(ch));
2965 /* Special hack for VT100 Linedraw glyphs */
2966 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2967 && tchar <= 0xBD) break_run = TRUE;
2969 if (!dbcs_screenfont && !dirty_line) {
2970 if ((tchar | tattr) == term->disptext[idx])
2972 else if (!dirty_run && ccount == 1)
2977 if ((dirty_run || last_run_dirty) && ccount > 0) {
2978 do_text(ctx, start, i, ch, ccount, attr, lattr);
2984 if (dbcs_screenfont)
2985 last_run_dirty = dirty_run;
2986 dirty_run = dirty_line;
2989 if ((tchar | tattr) != term->disptext[idx])
2991 ch[ccount++] = (char) tchar;
2992 term->disptext[idx] = tchar | tattr;
2994 /* If it's a wide char step along to the next one. */
2995 if (tattr & ATTR_WIDE) {
2996 if (++j < term->cols) {
2999 /* Cursor is here ? Ouch! */
3000 if (i == our_curs_y && j == term->curs.x) {
3001 cursor_background = *d;
3002 term->dispcurs = term->disptext + idx;
3004 if (term->disptext[idx] != *d)
3006 term->disptext[idx] = *d;
3010 if (dirty_run && ccount > 0) {
3011 do_text(ctx, start, i, ch, ccount, attr, lattr);
3015 /* Cursor on this line ? (and changed) */
3016 if (i == our_curs_y && (term->curstype != cursor || updated_line)) {
3017 ch[0] = (char) (cursor_background & CHAR_MASK);
3018 attr = (cursor_background & ATTR_MASK) | cursor;
3019 do_cursor(ctx, term->curs.x, i, ch, 1, attr, lattr);
3020 term->curstype = cursor;
3026 * Flick the switch that says if blinking things should be shown or hidden.
3029 void term_blink(Terminal *term, int flg)
3031 long now, blink_diff;
3033 now = GETTICKCOUNT();
3034 blink_diff = now - term->last_tblink;
3036 /* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
3037 if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
3038 term->last_tblink = now;
3039 term->tblinker = !term->tblinker;
3044 term->last_blink = now;
3048 blink_diff = now - term->last_blink;
3050 /* Make sure the cursor blinks no faster than system blink rate */
3051 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3054 term->last_blink = now;
3055 term->blinker = !term->blinker;
3059 * Invalidate the whole screen so it will be repainted in full.
3061 void term_invalidate(Terminal *term)
3065 for (i = 0; i < term->rows * (term->cols + 1); i++)
3066 term->disptext[i] = ATTR_INVALID;
3070 * Paint the window in response to a WM_PAINT message.
3072 void term_paint(Terminal *term, Context ctx,
3073 int left, int top, int right, int bottom)
3076 if (left < 0) left = 0;
3077 if (top < 0) top = 0;
3078 if (right >= term->cols) right = term->cols-1;
3079 if (bottom >= term->rows) bottom = term->rows-1;
3081 for (i = top; i <= bottom && i < term->rows; i++) {
3082 if ((term->disptext[i * (term->cols + 1) + term->cols] &
3083 LATTR_MODE) == LATTR_NORM)
3084 for (j = left; j <= right && j < term->cols; j++)
3085 term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
3087 for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++)
3088 term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
3091 /* This should happen soon enough, also for some reason it sometimes
3092 * fails to actually do anything when re-sizing ... painting the wrong
3096 do_paint (term, ctx, FALSE);
3100 * Attempt to scroll the scrollback. The second parameter gives the
3101 * position we want to scroll to; the first is +1 to denote that
3102 * this position is relative to the beginning of the scrollback, -1
3103 * to denote it is relative to the end, and 0 to denote that it is
3104 * relative to the current position.
3106 void term_scroll(Terminal *term, int rel, int where)
3108 int sbtop = -count234(term->scrollback);
3110 term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where;
3111 if (term->disptop < sbtop)
3112 term->disptop = sbtop;
3113 if (term->disptop > 0)
3119 static void clipme(Terminal *term, pos top, pos bottom, int rect)
3122 wchar_t *wbptr; /* where next char goes within workbuf */
3124 int wblen = 0; /* workbuf len */
3125 int buflen; /* amount of memory allocated to workbuf */
3127 buflen = 5120; /* Default size */
3128 workbuf = smalloc(buflen * sizeof(wchar_t));
3129 wbptr = workbuf; /* start filling here */
3130 old_top_x = top.x; /* needed for rect==1 */
3132 while (poslt(top, bottom)) {
3134 unsigned long *ldata = lineptr(top.y);
3138 * nlpos will point at the maximum position on this line we
3139 * should copy up to. So we start it at the end of the
3143 nlpos.x = term->cols;
3146 * ... move it backwards if there's unused space at the end
3147 * of the line (and also set `nl' if this is the case,
3148 * because in normal selection mode this means we need a
3149 * newline at the end)...
3151 if (!(ldata[term->cols] & LATTR_WRAPPED)) {
3152 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3153 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3154 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3155 && poslt(top, nlpos))
3157 if (poslt(nlpos, bottom))
3162 * ... and then clip it to the terminal x coordinate if
3163 * we're doing rectangular selection. (In this case we
3164 * still did the above, so that copying e.g. the right-hand
3165 * column from a table doesn't fill with spaces on the
3169 if (nlpos.x > bottom.x)
3171 nl = (top.y < bottom.y);
3174 while (poslt(top, bottom) && poslt(top, nlpos)) {
3177 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3179 wchar_t cbuf[16], *p;
3180 int uc = (ldata[top.x] & 0xFFFF);
3183 if (uc == UCSWIDE) {
3188 switch (uc & CSET_MASK) {
3191 uc = unitab_xterm[uc & 0xFF];
3195 uc = unitab_line[uc & 0xFF];
3198 uc = unitab_scoacs[uc&0xFF];
3201 switch (uc & CSET_MASK) {
3203 uc = unitab_font[uc & 0xFF];
3206 uc = unitab_oemcp[uc & 0xFF];
3210 set = (uc & CSET_MASK);
3211 c = (uc & CHAR_MASK);
3215 if (DIRECT_FONT(uc)) {
3216 if (c >= ' ' && c != 0x7F) {
3217 unsigned char buf[4];
3220 if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3222 buf[1] = (unsigned char) ldata[top.x + 1];
3223 rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3227 rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3231 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3238 for (p = cbuf; *p; p++) {
3239 /* Enough overhead for trailing NL and nul */
3240 if (wblen >= buflen - 16) {
3243 sizeof(wchar_t) * (buflen += 100));
3244 wbptr = workbuf + wblen;
3253 for (i = 0; i < sel_nl_sz; i++) {
3255 *wbptr++ = sel_nl[i];
3259 top.x = rect ? old_top_x : 0;
3261 #if SELECTION_NUL_TERMINATED
3265 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3266 if (buflen > 0) /* indicates we allocated this buffer */
3270 void term_copyall(Terminal *term)
3273 top.y = -count234(term->scrollback);
3275 clipme(term, top, term->curs, 0);
3279 * The wordness array is mainly for deciding the disposition of the
3280 * US-ASCII characters.
3282 static int wordtype(Terminal *term, int uc)
3285 int start, end, ctype;
3286 } *wptr, ucs_words[] = {
3292 0x037e, 0x037e, 1}, /* Greek question mark */
3294 0x0387, 0x0387, 1}, /* Greek ano teleia */
3296 0x055a, 0x055f, 1}, /* Armenian punctuation */
3298 0x0589, 0x0589, 1}, /* Armenian full stop */
3300 0x0700, 0x070d, 1}, /* Syriac punctuation */
3302 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3304 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3306 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3308 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3310 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3312 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3314 0x2000, 0x200a, 0}, /* Various spaces */
3316 0x2070, 0x207f, 2}, /* superscript */
3318 0x2080, 0x208f, 2}, /* subscript */
3320 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3322 0x3000, 0x3000, 0}, /* ideographic space */
3324 0x3001, 0x3020, 1}, /* ideographic punctuation */
3326 0x303f, 0x309f, 3}, /* Hiragana */
3328 0x30a0, 0x30ff, 3}, /* Katakana */
3330 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3332 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3334 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3336 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3338 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3340 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3342 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3344 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3346 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3351 uc &= (CSET_MASK | CHAR_MASK);
3353 switch (uc & CSET_MASK) {
3355 uc = unitab_xterm[uc & 0xFF];
3358 uc = unitab_line[uc & 0xFF];
3361 uc = unitab_scoacs[uc&0xFF];
3364 switch (uc & CSET_MASK) {
3366 uc = unitab_font[uc & 0xFF];
3369 uc = unitab_oemcp[uc & 0xFF];
3373 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3374 * fail as there's such a thing as a double width space. :-(
3376 if (dbcs_screenfont && font_codepage == line_codepage)
3380 return term->wordness[uc];
3382 for (wptr = ucs_words; wptr->start; wptr++) {
3383 if (uc >= wptr->start && uc <= wptr->end)
3391 * Spread the selection outwards according to the selection mode.
3393 static pos sel_spread_half(Terminal *term, pos p, int dir)
3395 unsigned long *ldata;
3397 int topy = -count234(term->scrollback);
3399 ldata = lineptr(p.y);
3401 switch (term->selmode) {
3404 * In this mode, every character is a separate unit, except
3405 * for runs of spaces at the end of a non-wrapping line.
3407 if (!(ldata[term->cols] & LATTR_WRAPPED)) {
3408 unsigned long *q = ldata + term->cols;
3409 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3411 if (q == ldata + term->cols)
3413 if (p.x >= q - ldata)
3414 p.x = (dir == -1 ? q - ldata : term->cols - 1);
3419 * In this mode, the units are maximal runs of characters
3420 * whose `wordness' has the same value.
3422 wvalue = wordtype(term, ldata[p.x]);
3425 if (p.x < term->cols-1) {
3426 if (wordtype(term, ldata[p.x + 1]) == wvalue)
3431 if (ldata[term->cols] & LATTR_WRAPPED) {
3432 unsigned long *ldata2;
3433 ldata2 = lineptr(p.y+1);
3434 if (wordtype(term, ldata2[0]) == wvalue) {
3447 if (wordtype(term, ldata[p.x - 1]) == wvalue)
3452 unsigned long *ldata2;
3455 ldata2 = lineptr(p.y-1);
3456 if ((ldata2[term->cols] & LATTR_WRAPPED) &&
3457 wordtype(term, ldata2[term->cols-1]) == wvalue) {
3469 * In this mode, every line is a unit.
3471 p.x = (dir == -1 ? 0 : term->cols - 1);
3477 static void sel_spread(Terminal *term)
3479 if (term->seltype == LEXICOGRAPHIC) {
3480 term->selstart = sel_spread_half(term, term->selstart, -1);
3481 decpos(term->selend);
3482 term->selend = sel_spread_half(term, term->selend, +1);
3483 incpos(term->selend);
3487 void term_do_paste(Terminal *term)
3492 get_clip(&data, &len);
3493 if (data && len > 0) {
3496 term_seen_key_event(term); /* pasted data counts */
3498 if (term->paste_buffer)
3499 sfree(term->paste_buffer);
3500 term->paste_pos = term->paste_hold = term->paste_len = 0;
3501 term->paste_buffer = smalloc(len * sizeof(wchar_t));
3504 while (p < data + len) {
3505 while (p < data + len &&
3506 !(p <= data + len - sel_nl_sz &&
3507 !memcmp(p, sel_nl, sizeof(sel_nl))))
3512 for (i = 0; i < p - q; i++) {
3513 term->paste_buffer[term->paste_len++] = q[i];
3517 if (p <= data + len - sel_nl_sz &&
3518 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3519 term->paste_buffer[term->paste_len++] = '\r';
3525 /* Assume a small paste will be OK in one go. */
3526 if (term->paste_len < 256) {
3527 luni_send(term->paste_buffer, term->paste_len, 0);
3528 if (term->paste_buffer)
3529 sfree(term->paste_buffer);
3530 term->paste_buffer = 0;
3531 term->paste_pos = term->paste_hold = term->paste_len = 0;
3534 get_clip(NULL, NULL);
3537 void term_mouse(Terminal *term, Mouse_Button b, Mouse_Action a, int x, int y,
3538 int shift, int ctrl, int alt)
3541 unsigned long *ldata;
3542 int raw_mouse = (term->xterm_mouse &&
3543 !cfg.no_mouse_rep &&
3544 !(cfg.mouse_override && shift));
3545 int default_seltype;
3549 if (a == MA_DRAG && !raw_mouse)
3550 term_scroll(term, 0, -1);
3552 if (y >= term->rows) {
3554 if (a == MA_DRAG && !raw_mouse)
3555 term_scroll(term, 0, +1);
3564 if (x >= term->cols)
3567 selpoint.y = y + term->disptop;
3569 ldata = lineptr(selpoint.y);
3570 if ((ldata[term->cols] & LATTR_MODE) != LATTR_NORM)
3574 int encstate = 0, r, c;
3576 static int is_down = 0;
3580 encstate = 0x20; /* left button down */
3591 case MBT_WHEEL_DOWN:
3594 default: break; /* placate gcc warning about enum use */
3598 if (term->xterm_mouse == 1)
3611 default: break; /* placate gcc warning about enum use */
3620 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3621 ldisc_send(abuf, 6, 0);
3625 b = translate_button(b);
3628 * Set the selection type (rectangular or normal) at the start
3629 * of a selection attempt, from the state of Alt.
3631 if (!alt ^ !cfg.rect_select)
3632 default_seltype = RECTANGULAR;
3634 default_seltype = LEXICOGRAPHIC;
3636 if (term->selstate == NO_SELECTION) {
3637 term->seltype = default_seltype;
3640 if (b == MBT_SELECT && a == MA_CLICK) {
3642 term->selstate = ABOUT_TO;
3643 term->seltype = default_seltype;
3644 term->selanchor = selpoint;
3645 term->selmode = SM_CHAR;
3646 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3648 term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3649 term->selstate = DRAGGING;
3650 term->selstart = term->selanchor = selpoint;
3651 term->selend = term->selstart;
3652 incpos(term->selend);
3654 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3655 (b == MBT_EXTEND && a != MA_RELEASE)) {
3656 if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
3658 if (b == MBT_EXTEND && a != MA_DRAG && term->selstate == SELECTED) {
3659 if (term->seltype == LEXICOGRAPHIC) {
3661 * For normal selection, we extend by moving
3662 * whichever end of the current selection is closer
3665 if (posdiff(selpoint, term->selstart) <
3666 posdiff(term->selend, term->selstart) / 2) {
3667 term->selanchor = term->selend;
3668 decpos(term->selanchor);
3670 term->selanchor = term->selstart;
3674 * For rectangular selection, we have a choice of
3675 * _four_ places to put selanchor and selpoint: the
3676 * four corners of the selection.
3678 if (2*selpoint.x < term->selstart.x + term->selend.x)
3679 term->selanchor.x = term->selend.x-1;
3681 term->selanchor.x = term->selstart.x;
3683 if (2*selpoint.y < term->selstart.y + term->selend.y)
3684 term->selanchor.y = term->selend.y;
3686 term->selanchor.y = term->selstart.y;
3688 term->selstate = DRAGGING;
3690 if (term->selstate != ABOUT_TO && term->selstate != DRAGGING)
3691 term->selanchor = selpoint;
3692 term->selstate = DRAGGING;
3693 if (term->seltype == LEXICOGRAPHIC) {
3695 * For normal selection, we set (selstart,selend) to
3696 * (selpoint,selanchor) in some order.
3698 if (poslt(selpoint, term->selanchor)) {
3699 term->selstart = selpoint;
3700 term->selend = term->selanchor;
3701 incpos(term->selend);
3703 term->selstart = term->selanchor;
3704 term->selend = selpoint;
3705 incpos(term->selend);
3709 * For rectangular selection, we may need to
3710 * interchange x and y coordinates (if the user has
3711 * dragged in the -x and +y directions, or vice versa).
3713 term->selstart.x = min(term->selanchor.x, selpoint.x);
3714 term->selend.x = 1+max(term->selanchor.x, selpoint.x);
3715 term->selstart.y = min(term->selanchor.y, selpoint.y);
3716 term->selend.y = max(term->selanchor.y, selpoint.y);
3719 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3720 if (term->selstate == DRAGGING) {
3722 * We've completed a selection. We now transfer the
3723 * data to the clipboard.
3725 clipme(term, term->selstart, term->selend,
3726 (term->seltype == RECTANGULAR));
3727 term->selstate = SELECTED;
3729 term->selstate = NO_SELECTION;
3730 } else if (b == MBT_PASTE
3732 #if MULTICLICK_ONLY_EVENT
3733 || a == MA_2CLK || a == MA_3CLK
3742 void term_nopaste(Terminal *term)
3744 if (term->paste_len == 0)
3746 sfree(term->paste_buffer);
3747 term->paste_buffer = NULL;
3748 term->paste_len = 0;
3751 int term_paste_pending(Terminal *term)
3753 return term->paste_len != 0;
3756 void term_paste(Terminal *term)
3758 long now, paste_diff;
3760 if (term->paste_len == 0)
3763 /* Don't wait forever to paste */
3764 if (term->paste_hold) {
3765 now = GETTICKCOUNT();
3766 paste_diff = now - term->last_paste;
3767 if (paste_diff >= 0 && paste_diff < 450)
3770 term->paste_hold = 0;
3772 while (term->paste_pos < term->paste_len) {
3774 while (n + term->paste_pos < term->paste_len) {
3775 if (term->paste_buffer[term->paste_pos + n++] == '\r')
3778 luni_send(term->paste_buffer + term->paste_pos, n, 0);
3779 term->paste_pos += n;
3781 if (term->paste_pos < term->paste_len) {
3782 term->paste_hold = 1;
3786 sfree(term->paste_buffer);
3787 term->paste_buffer = NULL;
3788 term->paste_len = 0;
3791 static void deselect(Terminal *term)
3793 term->selstate = NO_SELECTION;
3794 term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0;
3797 void term_deselect(Terminal *term)
3803 int term_ldisc(Terminal *term, int option)
3805 if (option == LD_ECHO)
3806 return term->term_echoing;
3807 if (option == LD_EDIT)
3808 return term->term_editing;
3813 * from_backend(), to get data from the backend for the terminal.
3815 int from_backend(void *vterm, int is_stderr, char *data, int len)
3817 Terminal *term = (Terminal *)vterm;
3821 bufchain_add(&term->inbuf, data, len);
3824 * term_out() always completely empties inbuf. Therefore,
3825 * there's no reason at all to return anything other than zero
3826 * from this function, because there _can't_ be a question of
3827 * the remote side needing to wait until term_out() has cleared
3830 * This is a slightly suboptimal way to deal with SSH2 - in
3831 * principle, the window mechanism would allow us to continue
3832 * to accept data on forwarded ports and X connections even
3833 * while the terminal processing was going slowly - but we
3834 * can't do the 100% right thing without moving the terminal
3835 * processing into a separate thread, and that might hurt
3836 * portability. So we manage stdout buffering the old SSH1 way:
3837 * if the terminal processing goes slowly, the whole SSH
3838 * connection stops accepting data until it's ready.
3840 * In practice, I can't imagine this causing serious trouble.