chiark / gitweb /
terminal: remove an unused initialization
[elogind.git] / src / libsystemd-terminal / term-screen.c
index b442b96050466f359aaacb94dfaebf2eb798dfda..5a053a31bd28371e82d23caf2ce80652f36734d8 100644 (file)
@@ -47,6 +47,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
 #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+<XY> 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+<ascii> */
+        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;
 }