📄 terminal.c
字号:
static void save_cursor(Terminal *term, int save){ if (save) { term->savecurs = term->curs; term->save_attr = term->curr_attr; term->save_cset = term->cset; term->save_utf = term->utf; term->save_wnext = term->wrapnext; term->save_csattr = term->cset_attr[term->cset]; term->save_sco_acs = term->sco_acs; } else { term->curs = term->savecurs; /* Make sure the window hasn't shrunk since the save */ if (term->curs.x >= term->cols) term->curs.x = term->cols - 1; if (term->curs.y >= term->rows) term->curs.y = term->rows - 1; term->curr_attr = term->save_attr; term->cset = term->save_cset; term->utf = term->save_utf; term->wrapnext = term->save_wnext; /* * wrapnext might reset to False if the x position is no * longer at the rightmost edge. */ if (term->wrapnext && term->curs.x < term->cols-1) term->wrapnext = FALSE; term->cset_attr[term->cset] = term->save_csattr; term->sco_acs = term->save_sco_acs; set_erase_char(term); }}/* * This function is called before doing _anything_ which affects * only part of a line of text. It is used to mark the boundary * between two character positions, and it indicates that some sort * of effect is going to happen on only one side of that boundary. * * The effect of this function is to check whether a CJK * double-width character is straddling the boundary, and to remove * it and replace it with two spaces if so. (Of course, one or * other of those spaces is then likely to be replaced with * something else again, as a result of whatever happens next.) * * Also, if the boundary is at the right-hand _edge_ of the screen, * it implies something deliberate is being done to the rightmost * column position; hence we must clear LATTR_WRAPPED2. * * The input to the function is the coordinates of the _second_ * character of the pair. */static void check_boundary(Terminal *term, int x, int y){ termline *ldata; /* Validate input coordinates, just in case. */ if (x == 0 || x > term->cols) return; ldata = scrlineptr(y); if (x == term->cols) { ldata->lattr &= ~LATTR_WRAPPED2; } else { if (ldata->chars[x].chr == UCSWIDE) { clear_cc(ldata, x-1); clear_cc(ldata, x); ldata->chars[x-1].chr = ' ' | CSET_ASCII; ldata->chars[x] = ldata->chars[x-1]; } }}/* * Erase a large portion of the screen: the whole screen, or the * whole line, or parts thereof. */static void erase_lots(Terminal *term, int line_only, int from_begin, int to_end){ pos start, end; int erase_lattr; int erasing_lines_from_top = 0; if (line_only) { start.y = term->curs.y; start.x = 0; end.y = term->curs.y + 1; end.x = 0; erase_lattr = FALSE; } else { start.y = 0; start.x = 0; end.y = term->rows; end.x = 0; erase_lattr = TRUE; } if (!from_begin) { start = term->curs; } if (!to_end) { end = term->curs; incpos(end); } if (!from_begin || !to_end) check_boundary(term, term->curs.x, term->curs.y); check_selection(term, start, end); /* Clear screen also forces a full window redraw, just in case. */ if (start.y == 0 && start.x == 0 && end.y == term->rows) term_invalidate(term); /* Lines scrolled away shouldn't be brought back on if the terminal * resizes. */ if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr) erasing_lines_from_top = 1; if (term->cfg.erase_to_scrollback && erasing_lines_from_top) { /* If it's a whole number of lines, starting at the top, and * we're fully erasing them, erase by scrolling and keep the * lines in the scrollback. */ int scrolllines = end.y; if (end.y == term->rows) { /* Shrink until we find a non-empty row.*/ scrolllines = find_last_nonempty_line(term, term->screen) + 1; } if (scrolllines > 0) scroll(term, 0, scrolllines - 1, scrolllines, TRUE); } else { termline *ldata = scrlineptr(start.y); while (poslt(start, end)) { if (start.x == term->cols) { if (!erase_lattr) ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2); else ldata->lattr = LATTR_NORM; } else { copy_termchar(ldata, start.x, &term->erase_char); } if (incpos(start) && start.y < term->rows) { ldata = scrlineptr(start.y); } } } /* After an erase of lines from the top of the screen, we shouldn't * bring the lines back again if the terminal enlarges (since the user or * application has explictly thrown them away). */ if (erasing_lines_from_top && !(term->alt_which)) term->tempsblines = 0;}/* * Insert or delete characters within the current line. n is +ve if * insertion is desired, and -ve for deletion. */static void insch(Terminal *term, int n){ int dir = (n < 0 ? -1 : +1); int m, j; pos cursplus; termline *ldata; n = (n < 0 ? -n : n); if (n > term->cols - term->curs.x) n = term->cols - term->curs.x; m = term->cols - term->curs.x - n; cursplus.y = term->curs.y; cursplus.x = term->curs.x + n; check_selection(term, term->curs, cursplus); check_boundary(term, term->curs.x, term->curs.y); if (dir < 0) check_boundary(term, term->curs.x + n, term->curs.y); ldata = scrlineptr(term->curs.y); if (dir < 0) { for (j = 0; j < m; j++) move_termchar(ldata, ldata->chars + term->curs.x + j, ldata->chars + term->curs.x + j + n); while (n--) copy_termchar(ldata, term->curs.x + m++, &term->erase_char); } else { for (j = m; j-- ;) move_termchar(ldata, ldata->chars + term->curs.x + j + n, ldata->chars + term->curs.x + j); while (n--) copy_termchar(ldata, term->curs.x + n, &term->erase_char); }}/* * Toggle terminal mode `mode' to state `state'. (`query' indicates * whether the mode is a DEC private one or a normal one.) */static void toggle_mode(Terminal *term, int mode, int query, int state){ unsigned long ticks; if (query) switch (mode) { case 1: /* DECCKM: application cursor keys */ term->app_cursor_keys = state; break; case 2: /* DECANM: VT52 mode */ term->vt52_mode = !state; if (term->vt52_mode) { term->blink_is_real = FALSE; term->vt52_bold = FALSE; } else { term->blink_is_real = term->cfg.blinktext; } break; case 3: /* DECCOLM: 80/132 columns */ deselect(term); if (!term->cfg.no_remote_resize) request_resize(term->frontend, state ? 132 : 80, term->rows); term->reset_132 = state; term->alt_t = term->marg_t = 0; term->alt_b = term->marg_b = term->rows - 1; move(term, 0, 0, 0); erase_lots(term, FALSE, TRUE, TRUE); break; case 5: /* DECSCNM: reverse video */ /* * Toggle reverse video. If we receive an OFF within the * visual bell timeout period after an ON, we trigger an * effective visual bell, so that ESC[?5hESC[?5l will * always be an actually _visible_ visual bell. */ ticks = GETTICKCOUNT(); /* turn off a previous vbell to avoid inconsistencies */ if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT) term->in_vbell = FALSE; if (term->rvideo && !state && /* we're turning it off... */ (ticks - term->rvbell_startpoint) < VBELL_TIMEOUT) {/*...soon*/ /* If there's no vbell timeout already, or this one lasts * longer, replace vbell_timeout with ours. */ if (!term->in_vbell || (term->rvbell_startpoint - term->vbell_startpoint < VBELL_TIMEOUT)) term->vbell_startpoint = term->rvbell_startpoint; term->in_vbell = TRUE; /* may clear rvideo but set in_vbell */ } else if (!term->rvideo && state) { /* This is an ON, so we notice the time and save it. */ term->rvbell_startpoint = ticks; } term->rvideo = state; term->seen_disp_event = TRUE; if (state) term_update(term); break; case 6: /* DECOM: DEC origin mode */ term->dec_om = state; break; case 7: /* DECAWM: auto wrap */ term->wrap = state; break; case 8: /* DECARM: auto key repeat */ term->repeat_off = !state; break; case 10: /* DECEDM: set local edit mode */ term->term_editing = state; if (term->ldisc) /* cause ldisc to notice changes */ ldisc_send(term->ldisc, NULL, 0, 0); break; case 25: /* DECTCEM: enable/disable cursor */ compatibility2(OTHER, VT220); term->cursor_on = state; term->seen_disp_event = TRUE; break; case 47: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE); term->disptop = 0; break; case 1000: /* xterm mouse 1 */ term->xterm_mouse = state ? 1 : 0; set_raw_mouse_mode(term->frontend, state); break; case 1002: /* xterm mouse 2 */ term->xterm_mouse = state ? 2 : 0; set_raw_mouse_mode(term->frontend, state); break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE); term->disptop = 0; break; case 1048: /* save/restore cursor */ if (!term->cfg.no_alt_screen) save_cursor(term, state); if (!state) term->seen_disp_event = TRUE; break; case 1049: /* cursor & alternate screen */ if (state && !term->cfg.no_alt_screen) save_cursor(term, state); if (!state) term->seen_disp_event = TRUE; compatibility(OTHER); deselect(term); swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE); if (!state && !term->cfg.no_alt_screen) save_cursor(term, state); term->disptop = 0; break; } else switch (mode) { case 4: /* IRM: set insert mode */ compatibility(VT102); term->insert = state; break; case 12: /* SRM: set echo mode */ term->term_echoing = !state; if (term->ldisc) /* cause ldisc to notice changes */ ldisc_send(term->ldisc, NULL, 0, 0); break; case 20: /* LNM: Return sends ... */ term->cr_lf_return = state; break; case 34: /* WYULCURM: Make cursor BIG */ compatibility2(OTHER, VT220); term->big_cursor = !state; }}/* * Process an OSC sequence: set window title or icon name. */static void do_osc(Terminal *term){ if (term->osc_w) { while (term->osc_strlen--) term->wordness[(unsigned char) term->osc_string[term->osc_strlen]] = term->esc_args[0]; } else { term->osc_string[term->osc_strlen] = '\0'; switch (term->esc_args[0]) { case 0: case 1: if (!term->cfg.no_remote_wintitle) set_icon(term->frontend, term->osc_string); if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: if (!term->cfg.no_remote_wintitle) set_title(term->frontend, term->osc_string); break; } }}/* * ANSI printing routines. */static void term_print_setup(Terminal *term){ bufchain_clear(&term->printer_buf); term->print_job = printer_start_job(term->cfg.printer);}static void term_print_flush(Terminal *term){ void *data; int len; int size; while ((size = bufchain_size(&term->printer_buf)) > 5) { bufchain_prefix(&term->printer_buf, &data, &len); if (len > size-5) len = size-5; printer_job_data(term->print_job, data, len); bufchain_consume(&term->printer_buf, len); }}static void term_print_finish(Terminal *term){ void *data; int len, size; char c; if (!term->printing && !term->only_printing) return; /* we need do nothing */ term_print_flush(term); while ((size = bufchain_size(&term->printer_buf)) > 0) { bufchain_prefix(&term->printer_buf, &data, &len); c = *(char *)data; if (c == '\033' || c == '\233') { bufchain_consume(&term->printer_buf, size); break; } else { printer_job_data(term->print_job, &c, 1); bufchain_consume(&term->printer_buf, 1); } } printer_finish_job(term->print_job); term->print_job = NULL; term->printing = term->only_printing = FALSE;}/* * Remove everything currently in `inbuf' and stick it up on the * in-memory display. There's a big state machine in here to * process escape sequences... */void term_out(Terminal *term){ unsigned long c; int unget; unsigned char localbuf[256], *chars; int nchars = 0; unget = -1; chars = NULL; /* placate compiler warnings */ while (nchars > 0 || bufchain_size(&term->inbuf) > 0) { if (unget == -1) { if (nchars == 0) { void *ret; bufchain_prefix(&term->inbuf, &ret, &nchars); if (nchars > sizeof(localbuf)) nchars = sizeof(localbuf); memcpy(localbuf, ret, nchars); bufchain_consume(&term->inbuf, nchars); chars = localbuf; assert(chars != NULL); } c = *chars++; nchars--; /* * Optionally log the session traffic to a file. Useful for * debugging and possibly also useful for actual logging. */ if (term->cfg.logtype == LGTYP_DEBUG && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG); } else { c = unget; unget = -1; } /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even * be able to display 8-bit characters, but I'll let that go 'cause * of i18n. */ /* * If we're printing, add the character to the printer * buffer. */ if (term->printing) { bufchain_add(&term->printer_buf, &c, 1); /* * If we're in print-only mode, we use a much simpler * state machine
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -