X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/0622ea7168aa265db70be885fbffb050f29f0205..8f9b6a1a1ab2eb675175662cf05204d4e86cb1e5:/terminal.c?ds=sidebyside diff --git a/terminal.c b/terminal.c index 1c817dac..27856896 100644 --- a/terminal.c +++ b/terminal.c @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -59,7 +57,7 @@ static unsigned long *disptext; /* buffer of text on real screen */ static unsigned long *dispcurs; /* location of cursor on real screen */ static unsigned long curstype; /* type of cursor on real screen */ -#define VBELL_TIMEOUT 100 /* millisecond len of visual bell */ +#define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */ struct beeptime { struct beeptime *next; @@ -99,7 +97,7 @@ static int wrap, wrapnext; /* wrap flags */ static int insert; /* insert-mode flag */ static int cset; /* 0 or 1: which char set */ static int save_cset, save_csattr; /* saved with cursor position */ -static int save_utf; /* saved with cursor position */ +static int save_utf, save_wnext; /* saved with cursor position */ static int rvideo; /* global reverse video flag */ static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */ static int cursor_on; /* cursor enabled flag */ @@ -115,6 +113,10 @@ static int vt52_bold; /* Force bold on non-bold colours */ static int utf_state; /* Is there a pending UTF-8 character */ static int utf_char; /* and what is it so far. */ static int utf_size; /* The size of the UTF character. */ +static int printing, only_printing; /* Are we doing ANSI printing? */ +static int print_state; /* state of print-end-sequence scan */ +static bufchain printer_buf; /* buffered data for printer */ +static printer_job *print_job; static int xterm_mouse; /* send mouse messages to app */ @@ -203,9 +205,10 @@ static int paste_len, paste_pos, paste_hold; */ static void do_paint(Context, int); static void erase_lots(int, int, int); -static void swap_screen(int); +static void swap_screen(int, int, int); static void update_sbar(void); static void deselect(void); +static void term_print_finish(void); /* * Resize a line to make it `cols' columns wide. @@ -302,15 +305,16 @@ static void power_on(void) blink_is_real = cfg.blinktext; erase_char = ERASE_CHAR; alt_which = 0; + term_print_finish(); { int i; for (i = 0; i < 256; i++) wordness[i] = cfg.wordness[i]; } if (screen) { - swap_screen(1); + swap_screen(1, FALSE, FALSE); erase_lots(FALSE, TRUE, TRUE); - swap_screen(0); + swap_screen(0, FALSE, FALSE); erase_lots(FALSE, TRUE, TRUE); } } @@ -324,10 +328,9 @@ void term_update(void) ctx = get_ctx(); if (ctx) { int need_sbar_update = seen_disp_event; - if ((seen_key_event && (cfg.scroll_on_key)) || - (seen_disp_event && (cfg.scroll_on_disp))) { + if (seen_disp_event && cfg.scroll_on_disp) { disptop = 0; /* return to main screen */ - seen_disp_event = seen_key_event = 0; + seen_disp_event = 0; need_sbar_update = TRUE; } if (need_sbar_update) @@ -339,6 +342,37 @@ void term_update(void) } /* + * Called from front end when a keypress occurs, to trigger + * anything magical that needs to happen in that situation. + */ +void term_seen_key_event(void) +{ + /* + * On any keypress, clear the bell overload mechanism + * completely, on the grounds that large numbers of + * beeps coming from deliberate key action are likely + * to be intended (e.g. beeps from filename completion + * blocking repeatedly). + */ + beep_overloaded = FALSE; + while (beephead) { + struct beeptime *tmp = beephead; + beephead = tmp->next; + sfree(tmp); + } + beeptail = NULL; + nbeeps = 0; + + /* + * Reset the scrollback on keypress, if we're doing that. + */ + if (cfg.scroll_on_key) { + disptop = 0; /* return to main screen */ + seen_disp_event = 1; + } +} + +/* * Same as power_on(), but an external function. */ void term_pwron(void) @@ -351,6 +385,30 @@ void term_pwron(void) } /* + * When the user reconfigures us, we need to check the forbidden- + * alternate-screen config option, disable raw mouse mode if the + * user has disabled mouse reporting, and abandon a print job if + * the user has disabled printing. + */ +void term_reconfig(void) +{ + if (cfg.no_alt_screen) + swap_screen(0, FALSE, FALSE); + if (cfg.no_mouse_rep) { + xterm_mouse = 0; + set_raw_mouse_mode(0); + } + if (cfg.no_remote_charset) { + cset_attr[0] = cset_attr[1] = ATTR_ASCII; + sco_acs = alt_sco_acs = 0; + utf = 0; + } + if (!*cfg.printer) { + term_print_finish(); + } +} + +/* * Clear the scrollback. */ void term_clrsb(void) @@ -396,7 +454,7 @@ void term_size(int newrows, int newcols, int newsavelines) return; /* nothing to do */ deselect(); - swap_screen(0); + swap_screen(0, FALSE, FALSE); alt_t = marg_t = 0; alt_b = marg_b = newrows - 1; @@ -499,7 +557,7 @@ void term_size(int newrows, int newcols, int newsavelines) savelines = newsavelines; fix_cpos; - swap_screen(save_alt_which); + swap_screen(save_alt_which, FALSE, FALSE); update_sbar(); term_update(); @@ -507,56 +565,76 @@ void term_size(int newrows, int newcols, int newsavelines) } /* - * Swap screens. + * Swap screens. If `reset' is TRUE and we have been asked to + * switch to the alternate screen, we must bring most of its + * configuration from the main screen and erase the contents of the + * alternate screen completely. (This is even true if we're already + * on it! Blame xterm.) */ -static void swap_screen(int which) +static void swap_screen(int which, int reset, int keep_cur_pos) { int t; tree234 *ttr; - if (which == alt_which) - return; + if (!which) + reset = FALSE; /* do no weird resetting if which==0 */ + + if (which != alt_which) { + alt_which = which; + + ttr = alt_screen; + alt_screen = screen; + screen = ttr; + t = curs.x; + if (!reset && !keep_cur_pos) + curs.x = alt_x; + alt_x = t; + t = curs.y; + if (!reset && !keep_cur_pos) + curs.y = alt_y; + alt_y = t; + t = marg_t; + if (!reset) marg_t = alt_t; + alt_t = t; + t = marg_b; + if (!reset) marg_b = alt_b; + alt_b = t; + t = dec_om; + if (!reset) dec_om = alt_om; + alt_om = t; + t = wrap; + if (!reset) wrap = alt_wrap; + alt_wrap = t; + t = wrapnext; + if (!reset) wrapnext = alt_wnext; + alt_wnext = t; + t = insert; + if (!reset) insert = alt_ins; + alt_ins = t; + t = cset; + if (!reset) cset = alt_cset; + alt_cset = t; + t = utf; + if (!reset) utf = alt_utf; + alt_utf = t; + t = sco_acs; + if (!reset) sco_acs = alt_sco_acs; + alt_sco_acs = t; + } - alt_which = which; - - ttr = alt_screen; - alt_screen = screen; - screen = ttr; - t = curs.x; - curs.x = alt_x; - alt_x = t; - t = curs.y; - curs.y = alt_y; - alt_y = t; - t = marg_t; - marg_t = alt_t; - alt_t = t; - t = marg_b; - marg_b = alt_b; - alt_b = t; - t = dec_om; - dec_om = alt_om; - alt_om = t; - t = wrap; - wrap = alt_wrap; - alt_wrap = t; - t = wrapnext; - wrapnext = alt_wnext; - alt_wnext = t; - t = insert; - insert = alt_ins; - alt_ins = t; - t = cset; - cset = alt_cset; - alt_cset = t; - t = utf; - utf = alt_utf; - alt_utf = t; - t = sco_acs; - sco_acs = alt_sco_acs; - alt_sco_acs = t; + if (reset && screen) { + /* + * Yes, this _is_ supposed to honour background-colour-erase. + */ + erase_lots(FALSE, TRUE, TRUE); + } - fix_cpos; + /* + * This might not be possible if we're called during + * initialisation. + */ + if (screen) + fix_cpos; } /* @@ -746,6 +824,7 @@ static void save_cursor(int save) save_attr = curr_attr; save_cset = cset; save_utf = utf; + save_wnext = wrapnext; save_csattr = cset_attr[cset]; save_sco_acs = sco_acs; } else { @@ -759,6 +838,13 @@ static void save_cursor(int save) curr_attr = save_attr; cset = save_cset; utf = save_utf; + wrapnext = save_wnext; + /* + * wrapnext might reset to False if the x position is no + * longer at the rightmost edge. + */ + if (wrapnext && curs.x < cols-1) + wrapnext = FALSE; cset_attr[cset] = save_csattr; sco_acs = save_sco_acs; fix_cpos; @@ -869,7 +955,8 @@ static void toggle_mode(int mode, int query, int state) break; case 3: /* 80/132 columns */ deselect(); - request_resize(state ? 132 : 80, rows); + if (!cfg.no_remote_resize) + request_resize(state ? 132 : 80, rows); reset_132 = state; break; case 5: /* reverse video */ @@ -879,7 +966,7 @@ static void toggle_mode(int mode, int query, int state) * effective visual bell, so that ESC[?5hESC[?5l will * always be an actually _visible_ visual bell. */ - ticks = GetTickCount(); + ticks = GETTICKCOUNT(); /* turn off a previous vbell to avoid inconsistencies */ if (ticks - vbell_startpoint >= VBELL_TIMEOUT) in_vbell = FALSE; @@ -921,7 +1008,7 @@ static void toggle_mode(int mode, int query, int state) case 47: /* alternate screen */ compatibility(OTHER); deselect(); - swap_screen(state); + swap_screen(cfg.no_alt_screen ? 0 : state, FALSE, FALSE); disptop = 0; break; case 1000: /* xterm mouse 1 */ @@ -932,6 +1019,27 @@ static void toggle_mode(int mode, int query, int state) xterm_mouse = state ? 2 : 0; set_raw_mouse_mode(state); break; + case 1047: /* alternate screen */ + compatibility(OTHER); + deselect(); + swap_screen(cfg.no_alt_screen ? 0 : state, TRUE, TRUE); + disptop = 0; + break; + case 1048: /* save/restore cursor */ + save_cursor(state); + if (!state) seen_disp_event = TRUE; + break; + case 1049: /* cursor & alternate screen */ + if (state) + save_cursor(state); + if (!state) seen_disp_event = TRUE; + compatibility(OTHER); + deselect(); + swap_screen(cfg.no_alt_screen ? 0 : state, TRUE, FALSE); + if (!state) + save_cursor(state); + disptop = 0; + break; } else switch (mode) { case 4: /* set insert mode */ @@ -964,16 +1072,65 @@ static void do_osc(void) switch (esc_args[0]) { case 0: case 1: - set_icon(osc_string); + if (!cfg.no_remote_wintitle) + set_icon(osc_string); if (esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: - set_title(osc_string); + if (!cfg.no_remote_wintitle) + set_title(osc_string); + break; + } + } +} + +/* + * ANSI printing routines. + */ +static void term_print_setup(void) +{ + bufchain_clear(&printer_buf); + print_job = printer_start_job(cfg.printer); +} +static void term_print_flush(void) +{ + void *data; + int len; + int size; + while ((size = bufchain_size(&printer_buf)) > 5) { + bufchain_prefix(&printer_buf, &data, &len); + if (len > size-5) + len = size-5; + printer_job_data(print_job, data, len); + bufchain_consume(&printer_buf, len); + } +} +static void term_print_finish(void) +{ + void *data; + int len, size; + char c; + + if (!printing && !only_printing) + return; /* we need do nothing */ + + term_print_flush(); + while ((size = bufchain_size(&printer_buf)) > 0) { + bufchain_prefix(&printer_buf, &data, &len); + c = *(char *)data; + if (c == '\033' || c == '\233') { + bufchain_consume(&printer_buf, size); break; + } else { + printer_job_data(print_job, &c, 1); + bufchain_consume(&printer_buf, 1); } } + printer_finish_job(print_job); + print_job = NULL; + printing = only_printing = FALSE; } /* @@ -989,6 +1146,7 @@ void term_out(void) unget = -1; + chars = NULL; /* placate compiler warnings */ while (nchars > 0 || bufchain_size(&inbuf) > 0) { if (unget == -1) { if (nchars == 0) { @@ -1020,6 +1178,38 @@ void term_out(void) * of i18n. */ + /* + * If we're printing, add the character to the printer + * buffer. + */ + if (printing) { + bufchain_add(&printer_buf, &c, 1); + + /* + * If we're in print-only mode, we use a much simpler + * state machine designed only to recognise the ESC[4i + * termination sequence. + */ + if (only_printing) { + if (c == '\033') + print_state = 1; + else if (c == (unsigned char)'\233') + print_state = 2; + else if (c == '[' && print_state == 1) + print_state = 2; + else if (c == '4' && print_state == 2) + print_state = 3; + else if (c == 'i' && print_state == 3) + print_state = 4; + else + print_state = 0; + if (print_state == 4) { + term_print_finish(); + } + continue; + } + } + /* First see about all those translations. */ if (termstate == TOPLEVEL) { if (in_utf) @@ -1164,7 +1354,8 @@ void term_out(void) curs.x--; wrapnext = FALSE; fix_cpos; - *cpos = (' ' | curr_attr | ATTR_ASCII); + if (!cfg.no_dbackspace) /* destructive bksp might be disabled */ + *cpos = (' ' | curr_attr | ATTR_ASCII); } else /* Or normal C0 controls. */ if ((c & -32) == 0 && termstate < DO_CTRLS) { @@ -1197,7 +1388,7 @@ void term_out(void) } else *d++ = *s; } - lpage_send(CP_ACP, abuf, d - abuf, 0); + lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0); } break; case '\007': @@ -1205,7 +1396,7 @@ void term_out(void) struct beeptime *newbeep; unsigned long ticks; - ticks = GetTickCount(); + ticks = GETTICKCOUNT(); if (!beep_overloaded) { newbeep = smalloc(sizeof(struct beeptime)); @@ -1256,12 +1447,12 @@ void term_out(void) * Perform an actual beep if we're not overloaded. */ if (!cfg.bellovl || !beep_overloaded) { - beep(cfg.beep); if (cfg.beep == BELL_VISUAL) { in_vbell = TRUE; vbell_startpoint = ticks; term_update(); - } + } else + beep(cfg.beep); } disptop = 0; } @@ -1509,7 +1700,8 @@ void term_out(void) compatibility(VT100); power_on(); if (reset_132) { - request_resize(80, rows); + if (!cfg.no_remote_resize) + request_resize(80, rows); reset_132 = 0; } fix_cpos; @@ -1573,46 +1765,56 @@ void term_out(void) case ANSI('A', '('): compatibility(VT100); - cset_attr[0] = ATTR_GBCHR; + if (!cfg.no_remote_charset) + cset_attr[0] = ATTR_GBCHR; break; case ANSI('B', '('): compatibility(VT100); - cset_attr[0] = ATTR_ASCII; + if (!cfg.no_remote_charset) + cset_attr[0] = ATTR_ASCII; break; case ANSI('0', '('): compatibility(VT100); - cset_attr[0] = ATTR_LINEDRW; + if (!cfg.no_remote_charset) + cset_attr[0] = ATTR_LINEDRW; break; case ANSI('U', '('): compatibility(OTHER); - cset_attr[0] = ATTR_SCOACS; + if (!cfg.no_remote_charset) + cset_attr[0] = ATTR_SCOACS; break; case ANSI('A', ')'): compatibility(VT100); - cset_attr[1] = ATTR_GBCHR; + if (!cfg.no_remote_charset) + cset_attr[1] = ATTR_GBCHR; break; case ANSI('B', ')'): compatibility(VT100); - cset_attr[1] = ATTR_ASCII; + if (!cfg.no_remote_charset) + cset_attr[1] = ATTR_ASCII; break; case ANSI('0', ')'): compatibility(VT100); - cset_attr[1] = ATTR_LINEDRW; + if (!cfg.no_remote_charset) + cset_attr[1] = ATTR_LINEDRW; break; case ANSI('U', ')'): compatibility(OTHER); - cset_attr[1] = ATTR_SCOACS; + if (!cfg.no_remote_charset) + cset_attr[1] = ATTR_SCOACS; break; case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): compatibility(OTHER); - utf = 1; + if (!cfg.no_remote_charset) + utf = 1; break; case ANSI('@', '%'): compatibility(OTHER); - utf = 0; + if (!cfg.no_remote_charset) + utf = 0; break; } break; @@ -1772,6 +1974,21 @@ void term_out(void) toggle_mode(esc_args[i], esc_query, TRUE); } break; + case 'i': + case ANSI_QUE('i'): + compatibility(VT100); + { + if (esc_nargs != 1) break; + if (esc_args[0] == 5 && *cfg.printer) { + printing = TRUE; + only_printing = !esc_query; + print_state = 0; + term_print_setup(); + } else if (esc_args[0] == 4 && printing) { + term_print_finish(); + } + } + break; case 'l': /* toggle modes to low */ case ANSI_QUE('l'): compatibility(VT100); @@ -1872,12 +2089,15 @@ void term_out(void) break; case 10: /* SCO acs off */ compatibility(SCOANSI); + if (cfg.no_remote_charset) break; sco_acs = 0; break; case 11: /* SCO acs on */ compatibility(SCOANSI); + if (cfg.no_remote_charset) break; sco_acs = 1; break; case 12: /* SCO acs on flipped */ compatibility(SCOANSI); + if (cfg.no_remote_charset) break; sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); @@ -1954,7 +2174,8 @@ void term_out(void) if (esc_nargs <= 1 && (esc_args[0] < 1 || esc_args[0] >= 24)) { compatibility(VT340TEXT); - request_resize(cols, def(esc_args[0], 24)); + if (!cfg.no_remote_resize) + request_resize(cols, def(esc_args[0], 24)); deselect(); } else if (esc_nargs >= 1 && esc_args[0] >= 1 && @@ -1972,8 +2193,9 @@ void term_out(void) break; case 3: if (esc_nargs >= 3) { - move_window(def(esc_args[1], 0), - def(esc_args[2], 0)); + if (!cfg.no_remote_resize) + move_window(def(esc_args[1], 0), + def(esc_args[2], 0)); } break; case 4: @@ -1993,8 +2215,9 @@ void term_out(void) break; case 8: if (esc_nargs >= 3) { - request_resize(def(esc_args[2], cfg.width), - def(esc_args[1], cfg.height)); + if (!cfg.no_remote_resize) + request_resize(def(esc_args[2], cfg.width), + def(esc_args[1], cfg.height)); } break; case 9: @@ -2076,7 +2299,8 @@ void term_out(void) */ compatibility(VT420); if (esc_nargs == 1 && esc_args[0] > 0) { - request_resize(cols, def(esc_args[0], cfg.height)); + if (!cfg.no_remote_resize) + request_resize(cols, def(esc_args[0], cfg.height)); deselect(); } break; @@ -2087,7 +2311,8 @@ void term_out(void) */ compatibility(VT340TEXT); if (esc_nargs <= 1) { - request_resize(def(esc_args[0], cfg.width), rows); + if (!cfg.no_remote_resize) + request_resize(def(esc_args[0], cfg.width), rows); deselect(); } break; @@ -2214,10 +2439,12 @@ void term_out(void) * Well we should do a soft reset at this point ... */ if (!has_compat(VT420) && has_compat(VT100)) { - if (reset_132) - request_resize(132, 24); - else - request_resize(80, 24); + if (!cfg.no_remote_resize) { + if (reset_132) + request_resize(132, 24); + else + request_resize(80, 24); + } } #endif break; @@ -2604,6 +2831,8 @@ void term_out(void) check_selection(curs, cursplus); } } + + term_print_flush(); } #if 0 @@ -2639,7 +2868,7 @@ static void do_paint(Context ctx, int may_optimise) * Check the visual bell state. */ if (in_vbell) { - ticks = GetTickCount(); + ticks = GETTICKCOUNT(); if (ticks - vbell_startpoint >= VBELL_TIMEOUT) in_vbell = FALSE; } @@ -2834,11 +3063,11 @@ void term_blink(int flg) static long last_tblink = 0; long now, blink_diff; - now = GetTickCount(); + now = GETTICKCOUNT(); blink_diff = now - last_tblink; - /* Make sure the text blinks no more than 2Hz */ - if (blink_diff < 0 || blink_diff > 450) { + /* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */ + if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) { last_tblink = now; tblinker = !tblinker; } @@ -2851,8 +3080,8 @@ void term_blink(int flg) blink_diff = now - last_blink; - /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */ - if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime()) + /* Make sure the cursor blinks no faster than system blink rate */ + if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK) return; last_blink = now; @@ -3019,16 +3248,14 @@ static void clipme(pos top, pos bottom, int rect) unsigned char buf[4]; WCHAR wbuf[4]; int rv; - if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) { + if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) { buf[0] = c; buf[1] = (unsigned char) ldata[top.x + 1]; - rv = MultiByteToWideChar(font_codepage, - 0, buf, 2, wbuf, 4); + rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4); top.x++; } else { buf[0] = c; - rv = MultiByteToWideChar(font_codepage, - 0, buf, 1, wbuf, 4); + rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4); } if (rv > 0) { @@ -3062,8 +3289,10 @@ static void clipme(pos top, pos bottom, int rect) top.y++; top.x = rect ? old_top_x : 0; } +#if SELECTION_NUL_TERMINATED wblen++; *wbptr++ = 0; +#endif write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */ if (buflen > 0) /* indicates we allocated this buffer */ sfree(workbuf); @@ -3292,9 +3521,11 @@ void term_do_paste(void) int len; get_clip(&data, &len); - if (data) { + if (data && len > 0) { wchar_t *p, *q; + term_seen_key_event(); /* pasted data counts */ + if (paste_buffer) sfree(paste_buffer); paste_pos = paste_hold = paste_len = 0; @@ -3339,7 +3570,9 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, { pos selpoint; unsigned long *ldata; - int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift); + int raw_mouse = (xterm_mouse && + !cfg.no_mouse_rep && + !(cfg.mouse_override && shift)); int default_seltype; if (y < 0) { @@ -3525,8 +3758,12 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, } else selstate = NO_SELECTION; } else if (b == MBT_PASTE - && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) { - term_do_paste(); + && (a == MA_CLICK +#if MULTICLICK_ONLY_EVENT + || a == MA_2CLK || a == MA_3CLK +#endif + )) { + request_paste(); } term_update(); @@ -3541,6 +3778,11 @@ void term_nopaste() paste_len = 0; } +int term_paste_pending(void) +{ + return paste_len != 0; +} + void term_paste() { static long last_paste = 0; @@ -3551,7 +3793,7 @@ void term_paste() /* Don't wait forever to paste */ if (paste_hold) { - now = GetTickCount(); + now = GETTICKCOUNT(); paste_diff = now - last_paste; if (paste_diff >= 0 && paste_diff < 450) return; @@ -3603,6 +3845,8 @@ int term_ldisc(int option) */ int from_backend(int is_stderr, char *data, int len) { + assert(len > 0); + bufchain_add(&inbuf, data, len); /*