X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fterm-screen.c;h=5a053a31bd28371e82d23caf2ce80652f36734d8;hp=b442b96050466f359aaacb94dfaebf2eb798dfda;hb=0077776275cb753e478e0f92d4065dec5276c44a;hpb=be5022138495d2e509735dec7486a040d3e2eb2d diff --git a/src/libsystemd-terminal/term-screen.c b/src/libsystemd-terminal/term-screen.c index b442b9605..5a053a31b 100644 --- a/src/libsystemd-terminal/term-screen.c +++ b/src/libsystemd-terminal/term-screen.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "macro.h" #include "term-internal.h" #include "util.h" @@ -69,18 +70,14 @@ int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *writ screen->cmd_fn_data = cmd_fn_data; screen->flags = TERM_FLAG_7BIT_MODE; screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400; - screen->gl = &screen->g0; - screen->gr = &screen->g1; screen->g0 = &term_unicode_lower; screen->g1 = &term_unicode_upper; screen->g2 = &term_unicode_lower; screen->g3 = &term_unicode_upper; - - screen->saved.cursor_x = 0; - screen->saved.cursor_y = 0; - screen->saved.attr = screen->attr; - screen->saved.gl = screen->gl; - screen->saved.gr = screen->gr; + screen->state.gl = &screen->g0; + screen->state.gr = &screen->g1; + screen->saved = screen->state; + screen->saved_alt = screen->saved; r = term_page_new(&screen->page_main); if (r < 0) @@ -223,7 +220,7 @@ static bool screen_tab_is_set(term_screen *screen, unsigned int pos) { static inline void screen_age_cursor(term_screen *screen) { term_cell *cell; - cell = term_page_get_cell(screen->page, screen->cursor_x, screen->cursor_y); + cell = term_page_get_cell(screen->page, screen->state.cursor_x, screen->state.cursor_y); if (cell) cell->age = screen->age; } @@ -236,21 +233,21 @@ static void screen_cursor_set(term_screen *screen, unsigned int x, unsigned int x = screen_clamp_x(screen, x); y = screen_clamp_y(screen, y); - if (x == screen->cursor_x && y == screen->cursor_y) + if (x == screen->state.cursor_x && y == screen->state.cursor_y) return; if (!(screen->flags & TERM_FLAG_HIDE_CURSOR)) screen_age_cursor(screen); - screen->cursor_x = x; - screen->cursor_y = y; + screen->state.cursor_x = x; + screen->state.cursor_y = y; if (!(screen->flags & TERM_FLAG_HIDE_CURSOR)) screen_age_cursor(screen); } static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned int y) { - if (screen->flags & TERM_FLAG_ORIGIN_MODE) { + if (screen->state.origin_mode) { x = screen_clamp_x(screen, x); y = screen_clamp_x(screen, y) + screen->page->scroll_idx; @@ -265,53 +262,53 @@ static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned } static void screen_cursor_left(term_screen *screen, unsigned int num) { - if (num > screen->cursor_x) - num = screen->cursor_x; + if (num > screen->state.cursor_x) + num = screen->state.cursor_x; - screen_cursor_set(screen, screen->cursor_x - num, screen->cursor_y); + screen_cursor_set(screen, screen->state.cursor_x - num, screen->state.cursor_y); } static void screen_cursor_left_tab(term_screen *screen, unsigned int num) { unsigned int i; - i = screen->cursor_x; + i = screen->state.cursor_x; while (i > 0 && num > 0) { if (screen_tab_is_set(screen, --i)) --num; } - screen_cursor_set(screen, i, screen->cursor_y); + screen_cursor_set(screen, i, screen->state.cursor_y); } static void screen_cursor_right(term_screen *screen, unsigned int num) { if (num > screen->page->width) num = screen->page->width; - screen_cursor_set(screen, screen->cursor_x + num, screen->cursor_y); + screen_cursor_set(screen, screen->state.cursor_x + num, screen->state.cursor_y); } static void screen_cursor_right_tab(term_screen *screen, unsigned int num) { unsigned int i; - i = screen->cursor_x; + i = screen->state.cursor_x; while (i + 1 < screen->page->width && num > 0) { if (screen_tab_is_set(screen, ++i)) --num; } - screen_cursor_set(screen, i, screen->cursor_y); + screen_cursor_set(screen, i, screen->state.cursor_y); } static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) { unsigned int max; - if (screen->cursor_y < screen->page->scroll_idx) { - if (num > screen->cursor_y) - num = screen->cursor_y; + if (screen->state.cursor_y < screen->page->scroll_idx) { + if (num > screen->state.cursor_y) + num = screen->state.cursor_y; - screen_cursor_set(screen, screen->cursor_x, screen->cursor_y - num); + screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num); } else { - max = screen->cursor_y - screen->page->scroll_idx; + max = screen->state.cursor_y - screen->page->scroll_idx; if (num > max) { if (num < 1) return; @@ -320,14 +317,14 @@ static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) screen_age_cursor(screen); if (scroll) - term_page_scroll_down(screen->page, num - max, &screen->attr, screen->age, NULL); + term_page_scroll_down(screen->page, num - max, &screen->state.attr, screen->age, NULL); - screen->cursor_y = screen->page->scroll_idx; + screen->state.cursor_y = screen->page->scroll_idx; if (!(screen->flags & TERM_FLAG_HIDE_CURSOR)) screen_age_cursor(screen); } else { - screen_cursor_set(screen, screen->cursor_x, screen->cursor_y - num); + screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num); } } } @@ -335,13 +332,13 @@ static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) static void screen_cursor_down(term_screen *screen, unsigned int num, bool scroll) { unsigned int max; - if (screen->cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) { + if (screen->state.cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) { if (num > screen->page->height) num = screen->page->height; - screen_cursor_set(screen, screen->cursor_x, screen->cursor_y - num); + screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num); } else { - max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->cursor_y; + max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->state.cursor_y; if (num > max) { if (num < 1) return; @@ -350,18 +347,44 @@ static void screen_cursor_down(term_screen *screen, unsigned int num, bool scrol screen_age_cursor(screen); if (scroll) - term_page_scroll_up(screen->page, num - max, &screen->attr, screen->age, screen->history); + term_page_scroll_up(screen->page, num - max, &screen->state.attr, screen->age, screen->history); - screen->cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1; + screen->state.cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1; if (!(screen->flags & TERM_FLAG_HIDE_CURSOR)) screen_age_cursor(screen); } else { - screen_cursor_set(screen, screen->cursor_x, screen->cursor_y + num); + screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y + num); } } } +static void screen_save_state(term_screen *screen, term_state *where) { + *where = screen->state; +} + +static void screen_restore_state(term_screen *screen, term_state *from) { + screen_cursor_set(screen, from->cursor_x, from->cursor_y); + screen->state = *from; +} + +static void screen_reset_page(term_screen *screen, term_page *page) { + term_page_set_scroll_region(page, 0, page->height); + term_page_erase(page, 0, 0, page->width, page->height, &screen->state.attr, screen->age, false); +} + +static void screen_change_alt(term_screen *screen, bool set) { + if (set) { + screen->page = screen->page_alt; + screen->history = NULL; + } else { + screen->page = screen->page_main; + screen->history = screen->history_main; + } + + screen->page->age = screen->age; +} + static inline void set_reset(term_screen *screen, unsigned int flag, bool set) { if (set) screen->flags |= flag; @@ -387,7 +410,7 @@ static void screen_mode_change(term_screen *screen, unsigned int mode, bool dec, * DECOM: origin-mode * TODO */ - set_reset(screen, TERM_FLAG_ORIGIN_MODE, set); + screen->state.origin_mode = set; } break; @@ -397,7 +420,7 @@ static void screen_mode_change(term_screen *screen, unsigned int mode, bool dec, * DECAWN: auto-wrap mode * TODO */ - set_reset(screen, TERM_FLAG_AUTO_WRAP, set); + screen->state.auto_wrap = set; } break; @@ -418,6 +441,74 @@ static void screen_mode_change(term_screen *screen, unsigned int mode, bool dec, * TODO */ set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set); + screen_age_cursor(screen); + } + + break; + case 47: + if (dec) { + /* + * XTERM-ASB: alternate-screen-buffer + * This enables/disables the alternate screen-buffer. + * It effectively saves the current page content and + * allows you to restore it when changing to the + * original screen-buffer again. + */ + screen_change_alt(screen, set); + } + + break; + case 1047: + if (dec) { + /* + * XTERM-ASBPE: alternate-screen-buffer-post-erase + * This is the same as XTERM-ASB but erases the + * alternate screen-buffer before switching back to the + * original buffer. Use it to discard any data on the + * alternate screen buffer when done. + */ + if (!set) + screen_reset_page(screen, screen->page_alt); + + screen_change_alt(screen, set); + } + + break; + case 1048: + if (dec) { + /* + * XTERM-ASBCS: alternate-screen-buffer-cursor-state + * This has the same effect as DECSC/DECRC, but uses a + * separate state buffer. It is usually used in + * combination with alternate screen buffers to provide + * stacked state storage. + */ + if (set) + screen_save_state(screen, &screen->saved_alt); + else + screen_restore_state(screen, &screen->saved_alt); + } + + break; + case 1049: + if (dec) { + /* + * XTERM-ASBX: alternate-screen-buffer-extended + * This combines XTERM-ASBPE and XTERM-ASBCS somewhat. + * When enabling, state is saved, alternate screen + * buffer is activated and cleared. + * When disabled, alternate screen buffer is cleared, + * then normal screen buffer is enabled and state is + * restored. + */ + if (set) + screen_save_state(screen, &screen->saved_alt); + + screen_reset_page(screen, screen->page_alt); + screen_change_alt(screen, set); + + if (!set) + screen_restore_state(screen, &screen->saved_alt); } break; @@ -433,19 +524,19 @@ static uint32_t screen_map(term_screen *screen, uint32_t val) { * identity. */ switch (val) { case 33 ... 126: - if (screen->glt) { - nval = (**screen->glt)[val - 32]; - screen->glt = NULL; + if (screen->state.glt) { + nval = (**screen->state.glt)[val - 32]; + screen->state.glt = NULL; } else { - nval = (**screen->gl)[val - 32]; + nval = (**screen->state.gl)[val - 32]; } break; case 160 ... 255: - if (screen->grt) { - nval = (**screen->grt)[val - 160]; - screen->grt = NULL; + if (screen->state.grt) { + nval = (**screen->state.grt)[val - 160]; + screen->state.grt = NULL; } else { - nval = (**screen->gr)[val - 160]; + nval = (**screen->state.gr)[val - 160]; } break; } @@ -469,20 +560,20 @@ static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) { term_char_t ch = TERM_CHAR_NULL; uint32_t c; - if (screen->cursor_x + 1 == screen->page->width + if (screen->state.cursor_x + 1 == screen->page->width && screen->flags & TERM_FLAG_PENDING_WRAP - && screen->flags & TERM_FLAG_AUTO_WRAP) { + && screen->state.auto_wrap) { screen_cursor_down(screen, 1, true); - screen_cursor_set(screen, 0, screen->cursor_y); + screen_cursor_set(screen, 0, screen->state.cursor_y); } screen_cursor_clear_wrap(screen); c = screen_map(screen, seq->terminator); ch = term_char_merge(ch, screen_map(screen, c)); - term_page_write(screen->page, screen->cursor_x, screen->cursor_y, ch, 1, &screen->attr, screen->age, false); + term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false); - if (screen->cursor_x + 1 == screen->page->width) + if (screen->state.cursor_x + 1 == screen->page->width) screen->flags |= TERM_FLAG_PENDING_WRAP; else screen_cursor_right(screen, 1); @@ -554,7 +645,7 @@ static int screen_CHA(term_screen *screen, const term_seq *seq) { pos = seq->args[0]; screen_cursor_clear_wrap(screen); - screen_cursor_set(screen, pos - 1, screen->cursor_y); + screen_cursor_set(screen, pos - 1, screen->state.cursor_y); return 0; } @@ -633,7 +724,7 @@ static int screen_CR(term_screen *screen, const term_seq *seq) { */ screen_cursor_clear_wrap(screen); - screen_cursor_set(screen, 0, screen->cursor_y); + screen_cursor_set(screen, 0, screen->state.cursor_y); return 0; } @@ -899,7 +990,7 @@ static int screen_DCH(term_screen *screen, const term_seq *seq) { num = seq->args[0]; screen_cursor_clear_wrap(screen); - term_page_delete_cells(screen->page, screen->cursor_x, screen->cursor_y, num, &screen->attr, screen->age); + term_page_delete_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age); return 0; } @@ -1252,14 +1343,7 @@ static int screen_DECRC(term_screen *screen, const term_seq *seq) { * state for the main display and the status line. */ - screen->attr = screen->saved.attr; - screen->gl = screen->saved.gl; - screen->gr = screen->saved.gr; - screen->glt = screen->saved.glt; - screen->grt = screen->saved.grt; - set_reset(screen, TERM_FLAG_AUTO_WRAP, screen->flags & TERM_FLAG_AUTO_WRAP); - set_reset(screen, TERM_FLAG_ORIGIN_MODE, screen->flags & TERM_FLAG_ORIGIN_MODE); - screen_cursor_set(screen, screen->saved.cursor_x, screen->saved.cursor_y); + screen_restore_state(screen, &screen->saved); return 0; } @@ -1466,15 +1550,7 @@ static int screen_DECSC(term_screen *screen, const term_seq *seq) { * * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent */ - screen->saved.cursor_x = screen->cursor_x; - screen->saved.cursor_y = screen->cursor_y; - screen->saved.attr = screen->attr; - screen->saved.gl = screen->gl; - screen->saved.gr = screen->gr; - screen->saved.glt = screen->glt; - screen->saved.grt = screen->grt; - screen->saved.flags = screen->flags & (TERM_FLAG_AUTO_WRAP - | TERM_FLAG_ORIGIN_MODE); + screen_save_state(screen, &screen->saved); return 0; } @@ -1501,10 +1577,10 @@ static int screen_DECSCA(term_screen *screen, const term_seq *seq) { switch (mode) { case 0: case 2: - screen->attr.protect = 0; + screen->state.attr.protect = 0; break; case 1: - screen->attr.protect = 1; + screen->state.attr.protect = 1; break; } @@ -1669,21 +1745,21 @@ static int screen_DECSED(term_screen *screen, const term_seq *seq) { switch (mode) { case 0: term_page_erase(screen->page, - screen->cursor_x, screen->cursor_y, + screen->state.cursor_x, screen->state.cursor_y, screen->page->width, screen->page->height, - &screen->attr, screen->age, true); + &screen->state.attr, screen->age, true); break; case 1: term_page_erase(screen->page, 0, 0, - screen->cursor_x, screen->cursor_y, - &screen->attr, screen->age, true); + screen->state.cursor_x, screen->state.cursor_y, + &screen->state.attr, screen->age, true); break; case 2: term_page_erase(screen->page, 0, 0, screen->page->width, screen->page->height, - &screen->attr, screen->age, true); + &screen->state.attr, screen->age, true); break; } @@ -1715,21 +1791,21 @@ static int screen_DECSEL(term_screen *screen, const term_seq *seq) { switch (mode) { case 0: term_page_erase(screen->page, - screen->cursor_x, screen->cursor_y, - screen->page->width, screen->cursor_y, - &screen->attr, screen->age, true); + screen->state.cursor_x, screen->state.cursor_y, + screen->page->width, screen->state.cursor_y, + &screen->state.attr, screen->age, true); break; case 1: term_page_erase(screen->page, - 0, screen->cursor_y, - screen->cursor_x, screen->cursor_y, - &screen->attr, screen->age, true); + 0, screen->state.cursor_y, + screen->state.cursor_x, screen->state.cursor_y, + &screen->state.attr, screen->age, true); break; case 2: term_page_erase(screen->page, - 0, screen->cursor_y, - screen->page->width, screen->cursor_y, - &screen->attr, screen->age, true); + 0, screen->state.cursor_y, + screen->page->width, screen->state.cursor_y, + &screen->state.attr, screen->age, true); break; } @@ -1975,8 +2051,7 @@ static int screen_DECSTBM(term_screen *screen, const term_seq *seq) { bottom = screen->page->height; } - term_page_set_scroll_region(screen->page_main, top - 1, bottom - top + 1); - term_page_set_scroll_region(screen->page_alt, top - 1, bottom - top + 1); + term_page_set_scroll_region(screen->page, top - 1, bottom - top + 1); screen_cursor_clear_wrap(screen); screen_cursor_set(screen, 0, 0); @@ -2076,7 +2151,7 @@ static int screen_DL(term_screen *screen, const term_seq *seq) { if (seq->args[0] > 0) num = seq->args[0]; - term_page_delete_lines(screen->page, screen->cursor_y, num, &screen->attr, screen->age); + term_page_delete_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age); return 0; } @@ -2121,9 +2196,9 @@ static int screen_ECH(term_screen *screen, const term_seq *seq) { num = seq->args[0]; term_page_erase(screen->page, - screen->cursor_x, screen->cursor_y, - screen->cursor_x + num, screen->cursor_y, - &screen->attr, screen->age, false); + screen->state.cursor_x, screen->state.cursor_y, + screen->state.cursor_x + num, screen->state.cursor_y, + &screen->state.attr, screen->age, false); return 0; } @@ -2152,21 +2227,21 @@ static int screen_ED(term_screen *screen, const term_seq *seq) { switch (mode) { case 0: term_page_erase(screen->page, - screen->cursor_x, screen->cursor_y, + screen->state.cursor_x, screen->state.cursor_y, screen->page->width, screen->page->height, - &screen->attr, screen->age, false); + &screen->state.attr, screen->age, false); break; case 1: term_page_erase(screen->page, 0, 0, - screen->cursor_x, screen->cursor_y, - &screen->attr, screen->age, false); + screen->state.cursor_x, screen->state.cursor_y, + &screen->state.attr, screen->age, false); break; case 2: term_page_erase(screen->page, 0, 0, screen->page->width, screen->page->height, - &screen->attr, screen->age, false); + &screen->state.attr, screen->age, false); break; } @@ -2196,21 +2271,21 @@ static int screen_EL(term_screen *screen, const term_seq *seq) { switch (mode) { case 0: term_page_erase(screen->page, - screen->cursor_x, screen->cursor_y, - screen->page->width, screen->cursor_y, - &screen->attr, screen->age, false); + screen->state.cursor_x, screen->state.cursor_y, + screen->page->width, screen->state.cursor_y, + &screen->state.attr, screen->age, false); break; case 1: term_page_erase(screen->page, - 0, screen->cursor_y, - screen->cursor_x, screen->cursor_y, - &screen->attr, screen->age, false); + 0, screen->state.cursor_y, + screen->state.cursor_x, screen->state.cursor_y, + &screen->state.attr, screen->age, false); break; case 2: term_page_erase(screen->page, - 0, screen->cursor_y, - screen->page->width, screen->cursor_y, - &screen->attr, screen->age, false); + 0, screen->state.cursor_y, + screen->page->width, screen->state.cursor_y, + &screen->state.attr, screen->age, false); break; } @@ -2269,7 +2344,7 @@ static int screen_HPA(term_screen *screen, const term_seq *seq) { num = seq->args[0]; screen_cursor_clear_wrap(screen); - screen_cursor_set(screen, num - 1, screen->cursor_y); + screen_cursor_set(screen, num - 1, screen->state.cursor_y); return 0; } @@ -2325,7 +2400,7 @@ static int screen_HTS(term_screen *screen, const term_seq *seq) { unsigned int pos; - pos = screen->cursor_x; + pos = screen->state.cursor_x; if (screen->page->width > 0) screen->tabs[pos / 8] |= 1U << (pos % 8); @@ -2370,7 +2445,7 @@ static int screen_ICH(term_screen *screen, const term_seq *seq) { num = seq->args[0]; screen_cursor_clear_wrap(screen); - term_page_insert_cells(screen->page, screen->cursor_x, screen->cursor_y, num, &screen->attr, screen->age); + term_page_insert_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age); return 0; } @@ -2396,7 +2471,7 @@ static int screen_IL(term_screen *screen, const term_seq *seq) { num = seq->args[0]; screen_cursor_clear_wrap(screen); - term_page_insert_lines(screen->page, screen->cursor_y, num, &screen->attr, screen->age); + term_page_insert_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age); return 0; } @@ -2422,7 +2497,7 @@ static int screen_LF(term_screen *screen, const term_seq *seq) { screen_cursor_down(screen, 1, true); if (screen->flags & TERM_FLAG_NEWLINE_MODE) - screen_cursor_left(screen, screen->cursor_x); + screen_cursor_left(screen, screen->state.cursor_x); return 0; } @@ -2433,7 +2508,7 @@ static int screen_LS1R(term_screen *screen, const term_seq *seq) { * Map G1 into GR. */ - screen->gr = &screen->g1; + screen->state.gr = &screen->g1; return 0; } @@ -2444,7 +2519,7 @@ static int screen_LS2(term_screen *screen, const term_seq *seq) { * Map G2 into GL. */ - screen->gl = &screen->g2; + screen->state.gl = &screen->g2; return 0; } @@ -2455,7 +2530,7 @@ static int screen_LS2R(term_screen *screen, const term_seq *seq) { * Map G2 into GR. */ - screen->gr = &screen->g2; + screen->state.gr = &screen->g2; return 0; } @@ -2466,7 +2541,7 @@ static int screen_LS3(term_screen *screen, const term_seq *seq) { * Map G3 into GL. */ - screen->gl = &screen->g3; + screen->state.gl = &screen->g3; return 0; } @@ -2477,7 +2552,7 @@ static int screen_LS3R(term_screen *screen, const term_seq *seq) { * Map G3 into GR. */ - screen->gr = &screen->g3; + screen->state.gr = &screen->g3; return 0; } @@ -2511,7 +2586,7 @@ static int screen_NEL(term_screen *screen, const term_seq *seq) { screen_cursor_clear_wrap(screen); screen_cursor_down(screen, 1, true); - screen_cursor_set(screen, 0, screen->cursor_y); + screen_cursor_set(screen, 0, screen->state.cursor_y); return 0; } @@ -2832,7 +2907,7 @@ static int screen_SD(term_screen *screen, const term_seq *seq) { if (seq->args[0] > 0) num = seq->args[0]; - term_page_scroll_down(screen->page, num, &screen->attr, screen->age, NULL); + term_page_scroll_down(screen->page, num, &screen->state.attr, screen->age, NULL); return 0; } @@ -2847,7 +2922,7 @@ static int screen_SGR(term_screen *screen, const term_seq *seq) { int v; if (seq->n_args < 1) { - zero(screen->attr); + zero(screen->state.attr); return 0; } @@ -2855,67 +2930,67 @@ static int screen_SGR(term_screen *screen, const term_seq *seq) { v = seq->args[i]; switch (v) { case 1: - screen->attr.bold = 1; + screen->state.attr.bold = 1; break; case 3: - screen->attr.italic = 1; + screen->state.attr.italic = 1; break; case 4: - screen->attr.underline = 1; + screen->state.attr.underline = 1; break; case 5: - screen->attr.blink = 1; + screen->state.attr.blink = 1; break; case 7: - screen->attr.inverse = 1; + screen->state.attr.inverse = 1; break; case 8: - screen->attr.hidden = 1; + screen->state.attr.hidden = 1; break; case 22: - screen->attr.bold = 0; + screen->state.attr.bold = 0; break; case 23: - screen->attr.italic = 0; + screen->state.attr.italic = 0; break; case 24: - screen->attr.underline = 0; + screen->state.attr.underline = 0; break; case 25: - screen->attr.blink = 0; + screen->state.attr.blink = 0; break; case 27: - screen->attr.inverse = 0; + screen->state.attr.inverse = 0; break; case 28: - screen->attr.hidden = 0; + screen->state.attr.hidden = 0; break; case 30 ... 37: - screen->attr.fg.ccode = v - 30 + TERM_CCODE_BLACK; + screen->state.attr.fg.ccode = v - 30 + TERM_CCODE_BLACK; break; case 39: - screen->attr.fg.ccode = 0; + screen->state.attr.fg.ccode = 0; break; case 40 ... 47: - screen->attr.bg.ccode = v - 40 + TERM_CCODE_BLACK; + screen->state.attr.bg.ccode = v - 40 + TERM_CCODE_BLACK; break; case 49: - screen->attr.bg.ccode = 0; + screen->state.attr.bg.ccode = 0; break; case 90 ... 97: - screen->attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK; + screen->state.attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK; break; case 100 ... 107: - screen->attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK; + screen->state.attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK; break; case 38: /* fallthrough */ case 48: if (v == 38) - dst = &screen->attr.fg; + dst = &screen->state.attr.fg; else - dst = &screen->attr.bg; + dst = &screen->state.attr.bg; ++i; if (i >= seq->n_args) @@ -2942,31 +3017,9 @@ static int screen_SGR(term_screen *screen, const term_seq *seq) { if (i >= seq->n_args || seq->args[i] < 0) break; + dst->ccode = TERM_CCODE_256; code = seq->args[i]; - if (code < 16) { - dst->ccode = code; - } else if (code < 232) { - static const uint8_t bval[] = { - 0x00, 0x5f, 0x87, - 0xaf, 0xd7, 0xff, - }; - - dst->ccode = TERM_CCODE_256; - dst->c256 = code; - code -= 16; - dst->blue = bval[code % 6]; - code /= 6; - dst->green = bval[code % 6]; - code /= 6; - dst->red = bval[code % 6]; - } else if (code < 256) { - dst->ccode = TERM_CCODE_256; - dst->c256 = code; - code = (code - 232) * 10 + 8; - dst->red = code; - dst->green = code; - dst->blue = code; - } + dst->c256 = code < 256 ? code : 0; break; } @@ -2975,7 +3028,7 @@ static int screen_SGR(term_screen *screen, const term_seq *seq) { case -1: /* fallthrough */ case 0: - zero(screen->attr); + zero(screen->state.attr); break; } } @@ -2989,7 +3042,7 @@ static int screen_SI(term_screen *screen, const term_seq *seq) { * Map G0 into GL. */ - screen->gl = &screen->g0; + screen->state.gl = &screen->g0; return 0; } @@ -3029,7 +3082,7 @@ static int screen_SO(term_screen *screen, const term_seq *seq) { * Map G1 into GL. */ - screen->gl = &screen->g1; + screen->state.gl = &screen->g1; return 0; } @@ -3050,7 +3103,7 @@ static int screen_SS2(term_screen *screen, const term_seq *seq) { * Temporarily map G2 into GL for the next graphics character. */ - screen->glt = &screen->g2; + screen->state.glt = &screen->g2; return 0; } @@ -3061,7 +3114,7 @@ static int screen_SS3(term_screen *screen, const term_seq *seq) { * Temporarily map G3 into GL for the next graphics character */ - screen->glt = &screen->g3; + screen->state.glt = &screen->g3; return 0; } @@ -3097,7 +3150,7 @@ static int screen_SU(term_screen *screen, const term_seq *seq) { if (seq->args[0] > 0) num = seq->args[0]; - term_page_scroll_up(screen->page, num, &screen->attr, screen->age, screen->history); + term_page_scroll_up(screen->page, num, &screen->state.attr, screen->age, screen->history); return 0; } @@ -3136,7 +3189,7 @@ static int screen_TBC(term_screen *screen, const term_seq *seq) { switch (mode) { case 0: - pos = screen->cursor_x; + pos = screen->state.cursor_x; if (screen->page->width > 0) screen->tabs[pos / 8] &= ~(1U << (pos % 8)); break; @@ -3167,7 +3220,7 @@ static int screen_VPA(term_screen *screen, const term_seq *seq) { pos = seq->args[0]; screen_cursor_clear_wrap(screen); - screen_cursor_set_rel(screen, screen->cursor_x, pos - 1); + screen_cursor_set_rel(screen, screen->state.cursor_x, pos - 1); return 0; } @@ -3755,6 +3808,12 @@ unsigned int term_screen_get_height(term_screen *screen) { return screen->page->height; } +uint64_t term_screen_get_age(term_screen *screen) { + assert_return(screen, 0); + + return screen->age; +} + int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) { uint32_t *ucs4_str; size_t i, j, ucs4_len; @@ -3763,6 +3822,8 @@ int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) { assert_return(screen, -EINVAL); + ++screen->age; + /* Feed bytes into utf8 decoder and handle parsed ucs4 chars. We always * treat data as UTF-8, but the parser makes sure to fall back to raw * 8bit mode if the stream is not valid UTF-8. This should be more than @@ -3784,12 +3845,329 @@ int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) { return 0; } -int term_screen_feed_keyboard(term_screen *screen, uint32_t keysym, uint32_t ascii, uint32_t ucs4, unsigned int mods) { +static char *screen_map_key(term_screen *screen, + char *p, + const uint32_t *keysyms, + size_t n_syms, + uint32_t ascii, + const uint32_t *ucs4, + unsigned int mods) { + char ch, ch2, ch_mods; + uint32_t v; + size_t i; + + /* TODO: All these key-mappings need to be verified. Public information + * on those mappings is pretty scarce and every emulator seems to do it + * slightly differently. + * A lot of mappings are also missing. */ + + if (n_syms < 1) + return p; + + if (n_syms == 1) + v = keysyms[0]; + else + v = XKB_KEY_NoSymbol; + + /* In some mappings, the modifiers are encoded as CSI parameters. The + * encoding is rather arbitrary, but seems to work. */ + ch_mods = 0; + switch (mods & (TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT | TERM_KBDMOD_CTRL)) { + case TERM_KBDMOD_SHIFT: + ch_mods = '2'; + break; + case TERM_KBDMOD_ALT: + ch_mods = '3'; + break; + case TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT: + ch_mods = '4'; + break; + case TERM_KBDMOD_CTRL: + ch_mods = '5'; + break; + case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT: + ch_mods = '6'; + break; + case TERM_KBDMOD_CTRL | TERM_KBDMOD_ALT: + ch_mods = '7'; + break; + case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT: + ch_mods = '8'; + break; + } + + /* A user might actually use multiple layouts for keyboard + * input. @keysyms[0] contains the actual keysym that the user + * used. But if this keysym is not in the ascii range, the + * input handler does check all other layouts that the user + * specified whether one of them maps the key to some ASCII + * keysym and provides this via @ascii. We always use the real + * keysym except when handling CTRL+ shortcuts we use the + * ascii keysym. This is for compatibility to xterm et. al. so + * ctrl+c always works regardless of the currently active + * keyboard layout. But if no ascii-sym is found, we still use + * the real keysym. */ + if (ascii == XKB_KEY_NoSymbol) + ascii = v; + + /* map CTRL+ */ + if (mods & TERM_KBDMOD_CTRL) { + switch (ascii) { + case 0x60 ... 0x7e: + /* Right hand side is mapped to the left and then + * treated equally. Fall through to left-hand side.. */ + ascii -= 0x20; + case 0x20 ... 0x5f: + /* Printable ASCII is mapped 1-1 in XKB and in + * combination with CTRL bit 7 is flipped. This + * is equivalent to the caret-notation. */ + *p++ = ascii ^ 0x40; + return p; + } + } + + /* map cursor keys */ + ch = 0; + switch (v) { + case XKB_KEY_Up: + ch = 'A'; + break; + case XKB_KEY_Down: + ch = 'B'; + break; + case XKB_KEY_Right: + ch = 'C'; + break; + case XKB_KEY_Left: + ch = 'D'; + break; + case XKB_KEY_Home: + ch = 'H'; + break; + case XKB_KEY_End: + ch = 'F'; + break; + } + if (ch) { + *p++ = 0x1b; + if (screen->flags & TERM_FLAG_CURSOR_KEYS) + *p++ = 'O'; + else + *p++ = '['; + if (ch_mods) { + *p++ = '1'; + *p++ = ';'; + *p++ = ch_mods; + } + *p++ = ch; + return p; + } + + /* map action keys */ + ch = 0; + switch (v) { + case XKB_KEY_Find: + ch = '1'; + break; + case XKB_KEY_Insert: + ch = '2'; + break; + case XKB_KEY_Delete: + ch = '3'; + break; + case XKB_KEY_Select: + ch = '4'; + break; + case XKB_KEY_Page_Up: + ch = '5'; + break; + case XKB_KEY_Page_Down: + ch = '6'; + break; + } + if (ch) { + *p++ = 0x1b; + *p++ = '['; + *p++ = ch; + if (ch_mods) { + *p++ = ';'; + *p++ = ch_mods; + } + *p++ = '~'; + return p; + } + + /* map lower function keys */ + ch = 0; + switch (v) { + case XKB_KEY_F1: + ch = 'P'; + break; + case XKB_KEY_F2: + ch = 'Q'; + break; + case XKB_KEY_F3: + ch = 'R'; + break; + case XKB_KEY_F4: + ch = 'S'; + break; + } + if (ch) { + if (ch_mods) { + *p++ = 0x1b; + *p++ = '['; + *p++ = '1'; + *p++ = ';'; + *p++ = ch_mods; + *p++ = ch; + } else { + *p++ = 0x1b; + *p++ = 'O'; + *p++ = ch; + } + + return p; + } + + /* map upper function keys */ + ch = 0; + ch2 = 0; + switch (v) { + case XKB_KEY_F5: + ch = '1'; + ch2 = '5'; + break; + case XKB_KEY_F6: + ch = '1'; + ch2 = '7'; + break; + case XKB_KEY_F7: + ch = '1'; + ch2 = '8'; + break; + case XKB_KEY_F8: + ch = '1'; + ch2 = '9'; + break; + case XKB_KEY_F9: + ch = '2'; + ch2 = '0'; + break; + case XKB_KEY_F10: + ch = '2'; + ch2 = '1'; + break; + case XKB_KEY_F11: + ch = '2'; + ch2 = '2'; + break; + case XKB_KEY_F12: + ch = '2'; + ch2 = '3'; + break; + } + if (ch) { + *p++ = 0x1b; + *p++ = '['; + *p++ = ch; + if (ch2) + *p++ = ch2; + if (ch_mods) { + *p++ = ';'; + *p++ = ch_mods; + } + *p++ = '~'; + return p; + } + + /* map special keys */ + switch (v) { + case 0xff08: /* XKB_KEY_BackSpace */ + case 0xff09: /* XKB_KEY_Tab */ + case 0xff0a: /* XKB_KEY_Linefeed */ + case 0xff0b: /* XKB_KEY_Clear */ + case 0xff15: /* XKB_KEY_Sys_Req */ + case 0xff1b: /* XKB_KEY_Escape */ + case 0xffff: /* XKB_KEY_Delete */ + *p++ = v - 0xff00; + return p; + case 0xff13: /* XKB_KEY_Pause */ + /* TODO: What should we do with this key? + * Sending XOFF is awful as there is no simple + * way on modern keyboards to send XON again. + * If someone wants this, we can re-eanble + * optionally. */ + return p; + case 0xff14: /* XKB_KEY_Scroll_Lock */ + /* TODO: What should we do on scroll-lock? + * Sending 0x14 is what the specs say but it is + * not used today the way most users would + * expect so we disable it. If someone wants + * this, we can re-enable it (optionally). */ + return p; + case XKB_KEY_Return: + *p++ = 0x0d; + if (screen->flags & TERM_FLAG_NEWLINE_MODE) + *p++ = 0x0a; + return p; + case XKB_KEY_ISO_Left_Tab: + *p++ = 0x09; + return p; + } + + /* map unicode keys */ + for (i = 0; i < n_syms; ++i) + p += term_utf8_encode(p, ucs4[i]); + + return p; +} + +int term_screen_feed_keyboard(term_screen *screen, + const uint32_t *keysyms, + size_t n_syms, + uint32_t ascii, + const uint32_t *ucs4, + unsigned int mods) { + _cleanup_free_ char *dyn = NULL; + static const size_t padding = 1; + char buf[128], *start, *p; + assert_return(screen, -EINVAL); - /* TODO */ + /* allocate buffer if too small */ + start = buf; + if (4 * n_syms + padding > sizeof(buf)) { + dyn = malloc(4 * n_syms + padding); + if (!dyn) + return -ENOMEM; - return 0; + start = dyn; + } + + /* reserve prefix space */ + start += padding; + p = start; + + p = screen_map_key(screen, p, keysyms, n_syms, ascii, ucs4, mods); + if (!p || p - start < 1) + return 0; + + /* The ALT modifier causes ESC to be prepended to any key-stroke. We + * already accounted for that buffer space above, so simply prepend it + * here. + * TODO: is altSendsEscape a suitable default? What are the semantics + * exactly? Is it used in C0/C1 conversion? Is it prepended if there + * already is an escape character? */ + if (mods & TERM_KBDMOD_ALT && *start != 0x1b) + *--start = 0x1b; + + /* turn C0 into C1 */ + if (!(screen->flags & TERM_FLAG_7BIT_MODE) && p - start >= 2) + if (start[0] == 0x1b && start[1] >= 0x40 && start[1] <= 0x5f) + *++start ^= 0x40; + + return screen_write(screen, start, p - start); } int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) { @@ -3799,11 +4177,11 @@ int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) { assert_return(screen, -EINVAL); - r = term_page_reserve(screen->page_main, x, y, &screen->attr, screen->age); + r = term_page_reserve(screen->page_main, x, y, &screen->state.attr, screen->age); if (r < 0) return r; - r = term_page_reserve(screen->page_alt, x, y, &screen->attr, screen->age); + r = term_page_reserve(screen->page_alt, x, y, &screen->state.attr, screen->age); if (r < 0) return r; @@ -3819,11 +4197,11 @@ int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) { for (i = (screen->page->width + 7) / 8 * 8; i < x; i += 8) screen->tabs[i / 8] = 0x1; - term_page_resize(screen->page_main, x, y, &screen->attr, screen->age, screen->history); - term_page_resize(screen->page_alt, x, y, &screen->attr, screen->age, NULL); + term_page_resize(screen->page_main, x, y, &screen->state.attr, screen->age, screen->history); + term_page_resize(screen->page_alt, x, y, &screen->state.attr, screen->age, NULL); - screen->cursor_x = screen_clamp_x(screen, screen->cursor_x); - screen->cursor_y = screen_clamp_x(screen, screen->cursor_y); + screen->state.cursor_x = screen_clamp_x(screen, screen->state.cursor_x); + screen->state.cursor_y = screen_clamp_x(screen, screen->state.cursor_y); screen_cursor_clear_wrap(screen); return 0; @@ -3834,29 +4212,27 @@ void term_screen_soft_reset(term_screen *screen) { assert(screen); - screen->gl = &screen->g0; - screen->gr = &screen->g1; - screen->glt = NULL; - screen->grt = NULL; screen->g0 = &term_unicode_lower; screen->g1 = &term_unicode_upper; screen->g2 = &term_unicode_lower; screen->g3 = &term_unicode_upper; + screen->state.attr = screen->default_attr; + screen->state.gl = &screen->g0; + screen->state.gr = &screen->g1; + screen->state.glt = NULL; + screen->state.grt = NULL; + screen->state.auto_wrap = 0; + screen->state.origin_mode = 0; + + screen->saved = screen->state; + screen->saved.cursor_x = 0; + screen->saved.cursor_y = 0; + screen->saved_alt = screen->saved; screen->page = screen->page_main; screen->history = screen->history_main; screen->flags = TERM_FLAG_7BIT_MODE; screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400; - screen->attr = screen->default_attr; - - screen->saved.cursor_x = 0; - screen->saved.cursor_y = 0; - screen->saved.attr = screen->attr; - screen->saved.gl = screen->gl; - screen->saved.gr = screen->gr; - screen->saved.glt = NULL; - screen->saved.grt = NULL; - screen->flags = 0; for (i = 0; i < screen->page->width; i += 8) screen->tabs[i / 8] = 0x1; @@ -3870,10 +4246,10 @@ void term_screen_hard_reset(term_screen *screen) { term_screen_soft_reset(screen); zero(screen->utf8); - screen->cursor_x = 0; - screen->cursor_y = 0; - term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->attr, screen->age, false); - term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->attr, screen->age, false); + screen->state.cursor_x = 0; + screen->state.cursor_y = 0; + term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false); + term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false); } int term_screen_set_answerback(term_screen *screen, const char *answerback) { @@ -3927,6 +4303,8 @@ int term_screen_draw(term_screen *screen, line_age = MAX(line->age, page->age); for (i = 0; i < page->width; ++i) { + term_attr attr; + cell = &line->cells[i]; cell_age = MAX(cell->age, line_age); @@ -3940,11 +4318,16 @@ int term_screen_draw(term_screen *screen, * renderers can assume ch_width is set properpy. */ cw = MAX(cell->cwidth, 1U); + attr = cell->attr; + if (i == screen->state.cursor_x && j == screen->state.cursor_y && + !(screen->flags & TERM_FLAG_HIDE_CURSOR)) + attr.inverse ^= 1; + r = draw_fn(screen, userdata, i, j, - &cell->attr, + &attr, ch_str, ch_n, cw); @@ -3954,7 +4337,7 @@ int term_screen_draw(term_screen *screen, } if (fb_age) - *fb_age = screen->age++; + *fb_age = screen->age; return 0; }