{"id":2222108,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2222108/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260410-qemu-vnc-v2-33-231416f76dc3@redhat.com/","project":{"id":14,"url":"http://patchwork.ozlabs.org/api/1.1/projects/14/?format=json","name":"QEMU Development","link_name":"qemu-devel","list_id":"qemu-devel.nongnu.org","list_email":"qemu-devel@nongnu.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260410-qemu-vnc-v2-33-231416f76dc3@redhat.com>","date":"2026-04-10T19:18:55","name":"[v2,33/67] ui/console-vc: move VT100 emulation into separate unit","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"201ea76558c360afff7a526e1cda9de2e2d84de2","submitter":{"id":66774,"url":"http://patchwork.ozlabs.org/api/1.1/people/66774/?format=json","name":"Marc-André Lureau","email":"marcandre.lureau@redhat.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260410-qemu-vnc-v2-33-231416f76dc3@redhat.com/mbox/","series":[{"id":499494,"url":"http://patchwork.ozlabs.org/api/1.1/series/499494/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/list/?series=499494","date":"2026-04-10T19:18:23","name":"ui: add standalone VNC server over D-Bus","version":2,"mbox":"http://patchwork.ozlabs.org/series/499494/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2222108/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2222108/checks/","tags":{},"headers":{"Return-Path":"<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=AEYxx1Qd;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"],"Received":["from lists.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fsmxp2WXTz1yGb\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 11 Apr 2026 05:26:22 +1000 (AEST)","from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wBHQy-00047P-RQ; Fri, 10 Apr 2026 15:22:28 -0400","from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1wBHQv-0003wd-Ar\n for qemu-devel@nongnu.org; Fri, 10 Apr 2026 15:22:25 -0400","from us-smtp-delivery-124.mimecast.com ([170.10.129.124])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1wBHQr-0001uN-4W\n for qemu-devel@nongnu.org; Fri, 10 Apr 2026 15:22:25 -0400","from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-31-kUN9afyjMwS_sj1z34ZWQQ-1; Fri,\n 10 Apr 2026 15:22:17 -0400","from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id DC36319560AA; Fri, 10 Apr 2026 19:22:16 +0000 (UTC)","from localhost (unknown [10.44.22.4])\n by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 550CB1955F2E; Fri, 10 Apr 2026 19:22:12 +0000 (UTC)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1775848940;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=5sprlmSV4Ur7GEPFpFtjT+bHK37r1JaLLvhvGQCYTXU=;\n b=AEYxx1Qdvx1FL5gI3ZQPFV9I1PNPVAqhhJv1YoP7X/PzgAApke1hBPHd23QSwqcr92BNY4\n 9WUmmYV6TpEwgGFFpHKBa6vgTibgOGlqP/3q4Z0Q3LvsTpOvG4sFxdODMMKA2U9vKVJ8fi\n VKiwcTTQlMII+RGAbqZrI4FD/O62LHE=","X-MC-Unique":"kUN9afyjMwS_sj1z34ZWQQ-1","X-Mimecast-MFC-AGG-ID":"kUN9afyjMwS_sj1z34ZWQQ_1775848937","From":"=?utf-8?q?Marc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>","Date":"Fri, 10 Apr 2026 23:18:55 +0400","Subject":"[PATCH v2 33/67] ui/console-vc: move VT100 emulation into separate\n unit","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"8bit","Message-Id":"<20260410-qemu-vnc-v2-33-231416f76dc3@redhat.com>","References":"<20260410-qemu-vnc-v2-0-231416f76dc3@redhat.com>","In-Reply-To":"<20260410-qemu-vnc-v2-0-231416f76dc3@redhat.com>","To":"qemu-devel@nongnu.org","Cc":"=?utf-8?q?Marc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>,\n\t=?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= <philmd@linaro.org>","X-Developer-Signature":"v=1; a=openpgp-sha256; l=72162;\n i=marcandre.lureau@redhat.com; h=from:subject:message-id;\n bh=N39MIt7cxo1bmXvUev5Ip0A4HDawvUj5+c2P3VT8sbE=;\n b=owEBbQKS/ZANAwAKAdro4Ql1lpzlAcsmYgBp2U0VYHWJMxcdz9UL0PC7FK6+KeR5l+HoseRfM\n NRDdvz59veJAjMEAAEKAB0WIQSHqb2TP4fGBtJ29i3a6OEJdZac5QUCadlNFQAKCRDa6OEJdZac\n 5QJED/4zDLKYPW1SLDYxaSMgzder2Mt195UyIXxwUBbTE1C8NNInhfuo4SGKyYWOXfQO78ZpxxX\n kfbOUOuyk5QWXDbMMzSPyFTGRb1XwV/jlJNqBkASUrugREvEokqFO9kLZTMLO0e65Dkruejh3VQ\n ZYPCsyr5nrpCmRXANDX/z1sbTBlLd1z2nwhs+i/Ezl0EvDtRQJYBwO4vFs4g5Wc1XlPn/V/Wi6f\n Xny4iAjd/2QGAKoSGOWA1l7Rw6v0/qPlGhGQ1WlDWbmcZztXAJzdafinfLCMO33al8Dr4E/IqoD\n GLRS2GWiNzu9zUKU9MCWaNzx9Rni0yICqz6sED15ICIO0tfh+LTT04kcqAZvlE9ECyVsvkAA+Of\n lNUh823gZXlUbEcDB933VJAk3DoGugWkCFM7MW3GYmZeBgaW0w60P6zg73LBbOb/FgXnuiMbIyd\n xrULCYjf4OxZbXSfSK0hM4Lk+k0eApLtA+lZ078u2EhikglLM1fGRo1Hc+j5EPpmD4JOvSAs1Jb\n 4aYSxGdGfWBa/6ckzFyPypRwDyGEXaQuMx6scSU9sqV5AZruEhLYJBqLrSeDpt/zYJS+zDP/ETx\n 0pvvKPRHqqNMhgvFr7CXYj+yoDhQ+lme6pILdPUHokJBnPeztwjNiYmfcITi8yIdmCEExyTbj9b\n LWAdiUohLybvctw==","X-Developer-Key":"i=marcandre.lureau@redhat.com; a=openpgp;\n fpr=87A9BD933F87C606D276F62DDAE8E10975969CE5","X-Scanned-By":"MIMEDefang 3.0 on 10.30.177.12","Received-SPF":"pass client-ip=170.10.129.124;\n envelope-from=marcandre.lureau@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com","X-Spam_score_int":"7","X-Spam_score":"0.7","X-Spam_bar":"/","X-Spam_report":"(0.7 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.54,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001,\n RCVD_IN_SBL_CSS=3.335, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001,\n RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001,\n SPF_PASS=-0.001 autolearn=no autolearn_force=no","X-Spam_action":"no action","X-BeenThere":"qemu-devel@nongnu.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"qemu development <qemu-devel.nongnu.org>","List-Unsubscribe":"<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>","List-Archive":"<https://lists.nongnu.org/archive/html/qemu-devel>","List-Post":"<mailto:qemu-devel@nongnu.org>","List-Help":"<mailto:qemu-devel-request@nongnu.org?subject=help>","List-Subscribe":"<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>","Errors-To":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org","Sender":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"},"content":"Move the VT100 terminal emulation code into dedicated ui/vt100.c and\nui/vt100.h files, completing the extraction from console-vc.c started\nin the previous patches. This makes the VT100 layer a self-contained\nmodule that can be reused independently of the chardev/console\ninfrastructure.\n\nThe code is moved as-is, with minor coding style fixes (adding missing\nbraces, fixing whitespace) applied during the move.\n\nReviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>\nSigned-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>\n---\n ui/console-priv.h     |    1 -\n ui/vt100.h            |   92 +++++\n ui/console-vc-stubs.c |    1 +\n ui/console-vc.c       | 1034 +------------------------------------------------\n ui/console.c          |    2 +\n ui/vt100.c            |  986 ++++++++++++++++++++++++++++++++++++++++++++++\n ui/meson.build        |    4 +-\n 7 files changed, 1085 insertions(+), 1035 deletions(-)","diff":"diff --git a/ui/console-priv.h b/ui/console-priv.h\nindex 39798c3e9d7..f8855753e30 100644\n--- a/ui/console-priv.h\n+++ b/ui/console-priv.h\n@@ -31,7 +31,6 @@ struct QemuConsole {\n };\n \n void qemu_text_console_update_size(QemuTextConsole *c);\n-void vt100_update_cursor(void);\n void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym);\n \n #endif\ndiff --git a/ui/vt100.h b/ui/vt100.h\nnew file mode 100644\nindex 00000000000..79cf8c23b2f\n--- /dev/null\n+++ b/ui/vt100.h\n@@ -0,0 +1,92 @@\n+/*\n+ * SPDX-License-Identifier: MIT\n+ * QEMU vt100\n+ */\n+#ifndef VT100_H\n+#define VT100_H\n+\n+#include \"ui/console.h\"\n+#include \"qemu/fifo8.h\"\n+#include \"qemu/queue.h\"\n+\n+typedef struct TextAttributes {\n+    uint8_t fgcol:4;\n+    uint8_t bgcol:4;\n+    uint8_t bold:1;\n+    uint8_t uline:1;\n+    uint8_t blink:1;\n+    uint8_t invers:1;\n+    uint8_t unvisible:1;\n+} TextAttributes;\n+\n+#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \\\n+    .fgcol = QEMU_COLOR_WHITE,                      \\\n+    .bgcol = QEMU_COLOR_BLACK                       \\\n+})\n+\n+typedef struct TextCell {\n+    uint8_t ch;\n+    TextAttributes t_attrib;\n+} TextCell;\n+\n+#define MAX_ESC_PARAMS 3\n+\n+enum TTYState {\n+    TTY_STATE_NORM,\n+    TTY_STATE_ESC,\n+    TTY_STATE_CSI,\n+    TTY_STATE_G0,\n+    TTY_STATE_G1,\n+    TTY_STATE_OSC,\n+};\n+\n+typedef struct QemuVT100 QemuVT100;\n+\n+struct QemuVT100 {\n+    pixman_image_t *image;\n+    void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);\n+\n+    int width;\n+    int height;\n+    int total_height;\n+    int backscroll_height;\n+    int x, y;\n+    int y_displayed;\n+    int y_base;\n+    TextCell *cells;\n+    int text_x[2], text_y[2], cursor_invalidate;\n+    int echo;\n+\n+    int update_x0;\n+    int update_y0;\n+    int update_x1;\n+    int update_y1;\n+\n+    enum TTYState state;\n+    int esc_params[MAX_ESC_PARAMS];\n+    int nb_esc_params;\n+    uint32_t utf8_state;     /* UTF-8 DFA decoder state */\n+    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */\n+    TextAttributes t_attrib; /* currently active text attributes */\n+    TextAttributes t_attrib_saved;\n+    int x_saved, y_saved;\n+    /* fifo for key pressed */\n+    Fifo8 out_fifo;\n+    void (*out_flush)(QemuVT100 *vt);\n+\n+    QTAILQ_ENTRY(QemuVT100) list;\n+};\n+\n+void vt100_init(QemuVT100 *vt,\n+                pixman_image_t *image,\n+                void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height),\n+                void (*out_flush)(QemuVT100 *vt));\n+void vt100_fini(QemuVT100 *vt);\n+\n+void vt100_update_cursor(void);\n+size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len);\n+void vt100_keysym(QemuVT100 *vt, int keysym);\n+void vt100_set_image(QemuVT100 *vt, pixman_image_t *image);\n+void vt100_refresh(QemuVT100 *vt);\n+\n+#endif\ndiff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c\nindex d911a82f263..30e4d101197 100644\n--- a/ui/console-vc-stubs.c\n+++ b/ui/console-vc-stubs.c\n@@ -9,6 +9,7 @@\n #include \"qemu/option.h\"\n #include \"chardev/char.h\"\n #include \"ui/console-priv.h\"\n+#include \"vt100.h\"\n \n void qemu_text_console_update_size(QemuTextConsole *c)\n {\ndiff --git a/ui/console-vc.c b/ui/console-vc.c\nindex 0d7b943fdc7..6e8f2552e41 100644\n--- a/ui/console-vc.c\n+++ b/ui/console-vc.c\n@@ -6,91 +6,15 @@\n \n #include \"chardev/char.h\"\n #include \"qapi/error.h\"\n-#include \"qemu/fifo8.h\"\n #include \"qemu/option.h\"\n-#include \"qemu/queue.h\"\n #include \"ui/console.h\"\n-#include \"ui/cp437.h\"\n #include \"ui/vgafont.h\"\n+#include \"ui/vt100.h\"\n \n #include \"pixman.h\"\n #include \"trace.h\"\n #include \"console-priv.h\"\n \n-#define DEFAULT_BACKSCROLL 512\n-#define CONSOLE_CURSOR_PERIOD 500\n-\n-typedef struct TextAttributes {\n-    uint8_t fgcol:4;\n-    uint8_t bgcol:4;\n-    uint8_t bold:1;\n-    uint8_t uline:1;\n-    uint8_t blink:1;\n-    uint8_t invers:1;\n-    uint8_t unvisible:1;\n-} TextAttributes;\n-\n-#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \\\n-    .fgcol = QEMU_COLOR_WHITE,                      \\\n-    .bgcol = QEMU_COLOR_BLACK                       \\\n-})\n-\n-typedef struct TextCell {\n-    uint8_t ch;\n-    TextAttributes t_attrib;\n-} TextCell;\n-\n-#define MAX_ESC_PARAMS 3\n-\n-enum TTYState {\n-    TTY_STATE_NORM,\n-    TTY_STATE_ESC,\n-    TTY_STATE_CSI,\n-    TTY_STATE_G0,\n-    TTY_STATE_G1,\n-    TTY_STATE_OSC,\n-};\n-\n-typedef struct QemuVT100 QemuVT100;\n-\n-struct QemuVT100 {\n-    pixman_image_t *image;\n-    void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);\n-\n-    int width;\n-    int height;\n-    int total_height;\n-    int backscroll_height;\n-    int x, y;\n-    int y_displayed;\n-    int y_base;\n-    TextCell *cells;\n-    int text_x[2], text_y[2], cursor_invalidate;\n-    int echo;\n-\n-    int update_x0;\n-    int update_y0;\n-    int update_x1;\n-    int update_y1;\n-\n-    enum TTYState state;\n-    int esc_params[MAX_ESC_PARAMS];\n-    int nb_esc_params;\n-    uint32_t utf8_state;     /* UTF-8 DFA decoder state */\n-    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */\n-    TextAttributes t_attrib; /* currently active text attributes */\n-    TextAttributes t_attrib_saved;\n-    int x_saved, y_saved;\n-    /* fifo for key pressed */\n-    Fifo8 out_fifo;\n-    void (*out_flush)(QemuVT100 *vt);\n-\n-    QTAILQ_ENTRY(QemuVT100) list;\n-};\n-\n-static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =\n-    QTAILQ_HEAD_INITIALIZER(vt100s);\n-\n typedef struct QemuTextConsole {\n     QemuConsole parent;\n \n@@ -116,32 +40,6 @@ struct VCChardev {\n };\n typedef struct VCChardev VCChardev;\n \n-static const pixman_color_t color_table_rgb[2][8] = {\n-    {   /* dark */\n-        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,\n-        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa),  /* blue */\n-        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00),  /* green */\n-        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa),  /* cyan */\n-        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00),  /* red */\n-        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa),  /* magenta */\n-        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00),  /* yellow */\n-        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR_GRAY,\n-    },\n-    {   /* bright */\n-        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,\n-        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff),  /* blue */\n-        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00),  /* green */\n-        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff),  /* cyan */\n-        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00),  /* red */\n-        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff),  /* magenta */\n-        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00),  /* yellow */\n-        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff),  /* white */\n-    }\n-};\n-\n-static bool cursor_visible_phase;\n-static QEMUTimer *cursor_timer;\n-\n static char *\n qemu_text_console_get_label(QemuConsole *c)\n {\n@@ -150,157 +48,6 @@ qemu_text_console_get_label(QemuConsole *c)\n     return tc->chr ? g_strdup(tc->chr->label) : NULL;\n }\n \n-static void image_fill_rect(pixman_image_t *image, int posx, int posy,\n-                            int width, int height, pixman_color_t color)\n-{\n-    pixman_rectangle16_t rect = {\n-        .x = posx, .y = posy, .width = width, .height = height\n-    };\n-\n-    pixman_image_fill_rectangles(PIXMAN_OP_SRC, image, &color, 1, &rect);\n-}\n-\n-/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */\n-static void image_bitblt(pixman_image_t *image,\n-                         int xs, int ys, int xd, int yd, int w, int h)\n-{\n-    pixman_image_composite(PIXMAN_OP_SRC,\n-                           image, NULL, image,\n-                           xs, ys, 0, 0, xd, yd, w, h);\n-}\n-\n-static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,\n-                            TextAttributes *t_attrib)\n-{\n-    static pixman_image_t *glyphs[256];\n-    pixman_color_t fgcol, bgcol;\n-\n-    assert(vt->image);\n-    if (t_attrib->invers) {\n-        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];\n-        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];\n-    } else {\n-        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];\n-        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];\n-    }\n-\n-    if (!glyphs[ch]) {\n-        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);\n-    }\n-    qemu_pixman_glyph_render(glyphs[ch], vt->image,\n-                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);\n-}\n-\n-static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)\n-{\n-    if (vt->update_x0 > x * FONT_WIDTH) {\n-        vt->update_x0 = x * FONT_WIDTH;\n-    }\n-    if (vt->update_y0 > y * FONT_HEIGHT) {\n-        vt->update_y0 = y * FONT_HEIGHT;\n-    }\n-    if (vt->update_x1 < (x + 1) * FONT_WIDTH) {\n-        vt->update_x1 = (x + 1) * FONT_WIDTH;\n-    }\n-    if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {\n-        vt->update_y1 = (y + 1) * FONT_HEIGHT;\n-    }\n-}\n-\n-static void vt100_show_cursor(QemuVT100 *vt, int show)\n-{\n-    TextCell *c;\n-    int y, y1;\n-    int x = vt->x;\n-\n-    vt->cursor_invalidate = 1;\n-\n-    if (x >= vt->width) {\n-        x = vt->width - 1;\n-    }\n-    y1 = (vt->y_base + vt->y) % vt->total_height;\n-    y = y1 - vt->y_displayed;\n-    if (y < 0) {\n-        y += vt->total_height;\n-    }\n-    if (y < vt->height) {\n-        c = &vt->cells[y1 * vt->width + x];\n-        if (show && cursor_visible_phase) {\n-            TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n-            t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */\n-            vt100_putcharxy(vt, x, y, c->ch, &t_attrib);\n-        } else {\n-            vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));\n-        }\n-        vt100_invalidate_xy(vt, x, y);\n-    }\n-}\n-\n-static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)\n-{\n-    vt->image_update(vt, x, y, width, height);\n-}\n-\n-static void vt100_refresh(QemuVT100 *vt)\n-{\n-    TextCell *c;\n-    int x, y, y1;\n-    int w = pixman_image_get_width(vt->image);\n-    int h = pixman_image_get_height(vt->image);\n-\n-    vt->text_x[0] = 0;\n-    vt->text_y[0] = 0;\n-    vt->text_x[1] = vt->width - 1;\n-    vt->text_y[1] = vt->height - 1;\n-    vt->cursor_invalidate = 1;\n-\n-    image_fill_rect(vt->image, 0, 0, w, h,\n-                    color_table_rgb[0][QEMU_COLOR_BLACK]);\n-    y1 = vt->y_displayed;\n-    for (y = 0; y < vt->height; y++) {\n-        c = vt->cells + y1 * vt->width;\n-        for (x = 0; x < vt->width; x++) {\n-            vt100_putcharxy(vt, x, y, c->ch,\n-                          &(c->t_attrib));\n-            c++;\n-        }\n-        if (++y1 == vt->total_height) {\n-            y1 = 0;\n-        }\n-    }\n-    vt100_show_cursor(vt, 1);\n-    vt100_image_update(vt, 0, 0, w, h);\n-}\n-\n-static void vt100_scroll(QemuVT100 *vt, int ydelta)\n-{\n-    int i, y1;\n-\n-    if (ydelta > 0) {\n-        for(i = 0; i < ydelta; i++) {\n-            if (vt->y_displayed == vt->y_base)\n-                break;\n-            if (++vt->y_displayed == vt->total_height)\n-                vt->y_displayed = 0;\n-        }\n-    } else {\n-        ydelta = -ydelta;\n-        i = vt->backscroll_height;\n-        if (i > vt->total_height - vt->height)\n-            i = vt->total_height - vt->height;\n-        y1 = vt->y_base - i;\n-        if (y1 < 0)\n-            y1 += vt->total_height;\n-        for(i = 0; i < ydelta; i++) {\n-            if (vt->y_displayed == y1)\n-                break;\n-            if (--vt->y_displayed < 0)\n-                vt->y_displayed = vt->total_height - 1;\n-        }\n-    }\n-    vt100_refresh(vt);\n-}\n-\n static void qemu_text_console_out_flush(QemuTextConsole *s)\n {\n     uint32_t len, avail;\n@@ -318,64 +65,6 @@ static void qemu_text_console_out_flush(QemuTextConsole *s)\n     }\n }\n \n-static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)\n-{\n-    uint32_t num_free;\n-\n-    num_free = fifo8_num_free(&vt->out_fifo);\n-    fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));\n-    vt->out_flush(vt);\n-}\n-\n-static int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len);\n-\n-static void vt100_keysym(QemuVT100 *vt, int keysym)\n-{\n-    uint8_t buf[16], *q;\n-    int c;\n-\n-    switch(keysym) {\n-    case QEMU_KEY_CTRL_UP:\n-        vt100_scroll(vt, -1);\n-        break;\n-    case QEMU_KEY_CTRL_DOWN:\n-        vt100_scroll(vt, 1);\n-        break;\n-    case QEMU_KEY_CTRL_PAGEUP:\n-        vt100_scroll(vt, -10);\n-        break;\n-    case QEMU_KEY_CTRL_PAGEDOWN:\n-        vt100_scroll(vt, 10);\n-        break;\n-    default:\n-        /* convert the QEMU keysym to VT100 key string */\n-        q = buf;\n-        if (keysym >= 0xe100 && keysym <= 0xe11f) {\n-            *q++ = '\\033';\n-            *q++ = '[';\n-            c = keysym - 0xe100;\n-            if (c >= 10)\n-                *q++ = '0' + (c / 10);\n-            *q++ = '0' + (c % 10);\n-            *q++ = '~';\n-        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {\n-            *q++ = '\\033';\n-            *q++ = '[';\n-            *q++ = keysym & 0xff;\n-        } else if (vt->echo && (keysym == '\\r' || keysym == '\\n')) {\n-            vt100_input(vt, (uint8_t *)\"\\r\", 1);\n-            *q++ = '\\n';\n-        } else {\n-            *q++ = keysym;\n-        }\n-        if (vt->echo) {\n-            vt100_input(vt, buf, q - buf);\n-        }\n-        vt100_write(vt, buf, q - buf);\n-        break;\n-    }\n-\n-}\n /* called when an ascii key is pressed */\n void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)\n {\n@@ -410,681 +99,10 @@ static void text_console_update(void *opaque, uint32_t *chardata)\n     }\n }\n \n-static void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)\n-{\n-    TextCell *cells, *c, *c1;\n-    int w1, x, y, last_width, w, h;\n-\n-    vt->image = image;\n-    w = pixman_image_get_width(image) / FONT_WIDTH;\n-    h = pixman_image_get_height(image) / FONT_HEIGHT;\n-    if (w == vt->width && h == vt->height) {\n-        return;\n-    }\n-\n-    last_width = vt->width;\n-    vt->width = w;\n-    vt->height = h;\n-\n-    w1 = MIN(vt->width, last_width);\n-\n-    cells = g_new(TextCell, vt->width * vt->total_height + 1);\n-    for (y = 0; y < vt->total_height; y++) {\n-        c = &cells[y * vt->width];\n-        if (w1 > 0) {\n-            c1 = &vt->cells[y * last_width];\n-            for (x = 0; x < w1; x++) {\n-                *c++ = *c1++;\n-            }\n-        }\n-        for (x = w1; x < vt->width; x++) {\n-            c->ch = ' ';\n-            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n-            c++;\n-        }\n-    }\n-    g_free(vt->cells);\n-    vt->cells = cells;\n-}\n-\n-static void vt100_put_lf(QemuVT100 *vt)\n-{\n-    TextCell *c;\n-    int x, y1;\n-\n-    vt->y++;\n-    if (vt->y >= vt->height) {\n-        vt->y = vt->height - 1;\n-\n-        if (vt->y_displayed == vt->y_base) {\n-            if (++vt->y_displayed == vt->total_height)\n-                vt->y_displayed = 0;\n-        }\n-        if (++vt->y_base == vt->total_height)\n-            vt->y_base = 0;\n-        if (vt->backscroll_height < vt->total_height)\n-            vt->backscroll_height++;\n-        y1 = (vt->y_base + vt->height - 1) % vt->total_height;\n-        c = &vt->cells[y1 * vt->width];\n-        for(x = 0; x < vt->width; x++) {\n-            c->ch = ' ';\n-            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n-            c++;\n-        }\n-        if (vt->y_displayed == vt->y_base) {\n-            vt->text_x[0] = 0;\n-            vt->text_y[0] = 0;\n-            vt->text_x[1] = vt->width - 1;\n-            vt->text_y[1] = vt->height - 1;\n-\n-            image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,\n-                         vt->width * FONT_WIDTH,\n-                         (vt->height - 1) * FONT_HEIGHT);\n-            image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,\n-                            vt->width * FONT_WIDTH, FONT_HEIGHT,\n-                            color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);\n-            vt->update_x0 = 0;\n-            vt->update_y0 = 0;\n-            vt->update_x1 = vt->width * FONT_WIDTH;\n-            vt->update_y1 = vt->height * FONT_HEIGHT;\n-        }\n-    }\n-}\n-\n-/* Set console attributes depending on the current escape codes.\n- * NOTE: I know this code is not very efficient (checking every color for it\n- * self) but it is more readable and better maintainable.\n- */\n-static void vt100_handle_escape(QemuVT100 *vt)\n-{\n-    int i;\n-\n-    for (i = 0; i < vt->nb_esc_params; i++) {\n-        switch (vt->esc_params[i]) {\n-            case 0: /* reset all console attributes to default */\n-                vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n-                break;\n-            case 1:\n-                vt->t_attrib.bold = 1;\n-                break;\n-            case 4:\n-                vt->t_attrib.uline = 1;\n-                break;\n-            case 5:\n-                vt->t_attrib.blink = 1;\n-                break;\n-            case 7:\n-                vt->t_attrib.invers = 1;\n-                break;\n-            case 8:\n-                vt->t_attrib.unvisible = 1;\n-                break;\n-            case 22:\n-                vt->t_attrib.bold = 0;\n-                break;\n-            case 24:\n-                vt->t_attrib.uline = 0;\n-                break;\n-            case 25:\n-                vt->t_attrib.blink = 0;\n-                break;\n-            case 27:\n-                vt->t_attrib.invers = 0;\n-                break;\n-            case 28:\n-                vt->t_attrib.unvisible = 0;\n-                break;\n-            /* set foreground color */\n-            case 30:\n-                vt->t_attrib.fgcol = QEMU_COLOR_BLACK;\n-                break;\n-            case 31:\n-                vt->t_attrib.fgcol = QEMU_COLOR_RED;\n-                break;\n-            case 32:\n-                vt->t_attrib.fgcol = QEMU_COLOR_GREEN;\n-                break;\n-            case 33:\n-                vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;\n-                break;\n-            case 34:\n-                vt->t_attrib.fgcol = QEMU_COLOR_BLUE;\n-                break;\n-            case 35:\n-                vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;\n-                break;\n-            case 36:\n-                vt->t_attrib.fgcol = QEMU_COLOR_CYAN;\n-                break;\n-            case 37:\n-                vt->t_attrib.fgcol = QEMU_COLOR_WHITE;\n-                break;\n-            /* set background color */\n-            case 40:\n-                vt->t_attrib.bgcol = QEMU_COLOR_BLACK;\n-                break;\n-            case 41:\n-                vt->t_attrib.bgcol = QEMU_COLOR_RED;\n-                break;\n-            case 42:\n-                vt->t_attrib.bgcol = QEMU_COLOR_GREEN;\n-                break;\n-            case 43:\n-                vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;\n-                break;\n-            case 44:\n-                vt->t_attrib.bgcol = QEMU_COLOR_BLUE;\n-                break;\n-            case 45:\n-                vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;\n-                break;\n-            case 46:\n-                vt->t_attrib.bgcol = QEMU_COLOR_CYAN;\n-                break;\n-            case 47:\n-                vt->t_attrib.bgcol = QEMU_COLOR_WHITE;\n-                break;\n-        }\n-    }\n-}\n-\n-static void vt100_update_xy(QemuVT100 *vt, int x, int y)\n-{\n-    TextCell *c;\n-    int y1, y2;\n-\n-    vt->text_x[0] = MIN(vt->text_x[0], x);\n-    vt->text_x[1] = MAX(vt->text_x[1], x);\n-    vt->text_y[0] = MIN(vt->text_y[0], y);\n-    vt->text_y[1] = MAX(vt->text_y[1], y);\n-\n-    y1 = (vt->y_base + y) % vt->total_height;\n-    y2 = y1 - vt->y_displayed;\n-    if (y2 < 0) {\n-        y2 += vt->total_height;\n-    }\n-    if (y2 < vt->height) {\n-        if (x >= vt->width) {\n-            x = vt->width - 1;\n-        }\n-        c = &vt->cells[y1 * vt->width + x];\n-        vt100_putcharxy(vt, x, y2, c->ch,\n-                      &(c->t_attrib));\n-        vt100_invalidate_xy(vt, x, y2);\n-    }\n-}\n-\n-static void vt100_clear_xy(QemuVT100 *vt, int x, int y)\n-{\n-    int y1 = (vt->y_base + y) % vt->total_height;\n-    if (x >= vt->width) {\n-        x = vt->width - 1;\n-    }\n-    TextCell *c = &vt->cells[y1 * vt->width + x];\n-    c->ch = ' ';\n-    c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n-    vt100_update_xy(vt, x, y);\n-}\n-\n-/*\n- * UTF-8 DFA decoder by Bjoern Hoehrmann.\n- * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n- * See https://github.com/polijan/utf8_decode for details.\n- *\n- * SPDX-License-Identifier: MIT\n- */\n-#define BH_UTF8_ACCEPT 0\n-#define BH_UTF8_REJECT 12\n-\n-static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)\n-{\n-    static const uint8_t utf8d[] = {\n-        /* character class lookup */\n-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n-        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n-        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n-        8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,\n-        10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,\n-\n-        /* state transition lookup */\n-        0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,\n-        12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,\n-        12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,\n-        12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,\n-        12,36,12,12,12,12,12,12,12,12,12,12,\n-    };\n-    uint32_t type = utf8d[byte];\n-\n-    *codep = (*state != BH_UTF8_ACCEPT) ?\n-        (byte & 0x3fu) | (*codep << 6) :\n-        (0xffu >> type) & (byte);\n-\n-    *state = utf8d[256 + *state + type];\n-    return *state;\n-}\n-\n-static void vt100_put_one(QemuVT100 *vt, int ch)\n-{\n-    TextCell *c;\n-    int y1;\n-    if (vt->x >= vt->width) {\n-        /* line wrap */\n-        vt->x = 0;\n-        vt100_put_lf(vt);\n-    }\n-    y1 = (vt->y_base + vt->y) % vt->total_height;\n-    c = &vt->cells[y1 * vt->width + vt->x];\n-    c->ch = ch;\n-    c->t_attrib = vt->t_attrib;\n-    vt100_update_xy(vt, vt->x, vt->y);\n-    vt->x++;\n-}\n-\n-/* set cursor, checking bounds */\n-static void vt100_set_cursor(QemuVT100 *vt, int x, int y)\n-{\n-    if (x < 0) {\n-        x = 0;\n-    }\n-    if (y < 0) {\n-        y = 0;\n-    }\n-    if (y >= vt->height) {\n-        y = vt->height - 1;\n-    }\n-    if (x >= vt->width) {\n-        x = vt->width - 1;\n-    }\n-\n-    vt->x = x;\n-    vt->y = y;\n-}\n-\n-/**\n- * vc_csi_P() - (DCH) deletes one or more characters from the cursor\n- * position to the right. As characters are deleted, the remaining\n- * characters between the cursor and right margin move to the\n- * left. Character attributes move with the characters.\n- */\n-static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)\n-{\n-    TextCell *c1, *c2;\n-    unsigned int x1, x2, y;\n-    unsigned int end, len;\n-\n-    if (!nr) {\n-        nr = 1;\n-    }\n-    if (nr > vt->width - vt->x) {\n-        nr = vt->width - vt->x;\n-        if (!nr) {\n-            return;\n-        }\n-    }\n-\n-    x1 = vt->x;\n-    x2 = vt->x + nr;\n-    len = vt->width - x2;\n-    if (len) {\n-        y = (vt->y_base + vt->y) % vt->total_height;\n-        c1 = &vt->cells[y * vt->width + x1];\n-        c2 = &vt->cells[y * vt->width + x2];\n-        memmove(c1, c2, len * sizeof(*c1));\n-        for (end = x1 + len; x1 < end; x1++) {\n-            vt100_update_xy(vt, x1, vt->y);\n-        }\n-    }\n-    /* Clear the rest */\n-    for (; x1 < vt->width; x1++) {\n-        vt100_clear_xy(vt, x1, vt->y);\n-    }\n-}\n-\n-/**\n- * vc_csi_at() - (ICH) inserts `nr` blank characters with the default\n- * character attribute. The cursor remains at the beginning of the\n- * blank characters. Text between the cursor and right margin moves to\n- * the right. Characters scrolled past the right margin are lost.\n- */\n-static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)\n-{\n-    TextCell *c1, *c2;\n-    unsigned int x1, x2, y;\n-    unsigned int end, len;\n-\n-    if (!nr) {\n-        nr = 1;\n-    }\n-    if (nr > vt->width - vt->x) {\n-        nr = vt->width - vt->x;\n-        if (!nr) {\n-            return;\n-        }\n-    }\n-\n-    x1 = vt->x + nr;\n-    x2 = vt->x;\n-    len = vt->width - x1;\n-    if (len) {\n-        y = (vt->y_base + vt->y) % vt->total_height;\n-        c1 = &vt->cells[y * vt->width + x1];\n-        c2 = &vt->cells[y * vt->width + x2];\n-        memmove(c1, c2, len * sizeof(*c1));\n-        for (end = x1 + len; x1 < end; x1++) {\n-            vt100_update_xy(vt, x1, vt->y);\n-        }\n-    }\n-    /* Insert blanks */\n-    for (x1 = vt->x; x1 < vt->x + nr; x1++) {\n-        vt100_clear_xy(vt, x1, vt->y);\n-    }\n-}\n-\n-/**\n- * vt100_save_cursor() - saves cursor position and character attributes.\n- */\n-static void vt100_save_cursor(QemuVT100 *vt)\n-{\n-    vt->x_saved = vt->x;\n-    vt->y_saved = vt->y;\n-    vt->t_attrib_saved = vt->t_attrib;\n-}\n-\n-/**\n- * vt100_restore_cursor() - restores cursor position and character\n- * attributes from saved state.\n- */\n-static void vt100_restore_cursor(QemuVT100 *vt)\n-{\n-    vt->x = vt->x_saved;\n-    vt->y = vt->y_saved;\n-    vt->t_attrib = vt->t_attrib_saved;\n-}\n-\n-static void vt100_putchar(QemuVT100 *vt, int ch)\n-{\n-    int i;\n-    int x, y;\n-    g_autofree char *response = NULL;\n-\n-    switch (vt->state) {\n-    case TTY_STATE_NORM:\n-        /* Feed byte through the UTF-8 DFA decoder */\n-        if (ch >= 0x80) {\n-            switch (bh_utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {\n-            case BH_UTF8_ACCEPT:\n-                vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));\n-                break;\n-            case BH_UTF8_REJECT:\n-                /* Reset state so the decoder can resync */\n-                vt->utf8_state = BH_UTF8_ACCEPT;\n-                break;\n-            default:\n-                /* Need more bytes */\n-                break;\n-            }\n-            break;\n-        }\n-        /* ASCII byte: abort any pending UTF-8 sequence */\n-        vt->utf8_state = BH_UTF8_ACCEPT;\n-        switch(ch) {\n-        case '\\r':  /* carriage return */\n-            vt->x = 0;\n-            break;\n-        case '\\n':  /* newline */\n-            vt100_put_lf(vt);\n-            break;\n-        case '\\b':  /* backspace */\n-            if (vt->x > 0)\n-                vt->x--;\n-            break;\n-        case '\\t':  /* tabspace */\n-            if (vt->x + (8 - (vt->x % 8)) > vt->width) {\n-                vt->x = 0;\n-                vt100_put_lf(vt);\n-            } else {\n-                vt->x = vt->x + (8 - (vt->x % 8));\n-            }\n-            break;\n-        case '\\a':  /* alert aka. bell */\n-            /* TODO: has to be implemented */\n-            break;\n-        case 14:\n-            /* SO (shift out), character set 1 (ignored) */\n-            break;\n-        case 15:\n-            /* SI (shift in), character set 0 (ignored) */\n-            break;\n-        case 27:    /* esc (introducing an escape sequence) */\n-            vt->state = TTY_STATE_ESC;\n-            break;\n-        default:\n-            vt100_put_one(vt, ch);\n-            break;\n-        }\n-        break;\n-    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */\n-        if (ch == '[') {\n-            for(i=0;i<MAX_ESC_PARAMS;i++)\n-                vt->esc_params[i] = 0;\n-            vt->nb_esc_params = 0;\n-            vt->state = TTY_STATE_CSI;\n-        } else if (ch == '(') {\n-            vt->state = TTY_STATE_G0;\n-        } else if (ch == ')') {\n-            vt->state = TTY_STATE_G1;\n-        } else if (ch == ']' || ch == 'P' || ch == 'X'\n-                   || ch == '^' || ch == '_') {\n-            /* String sequences: OSC, DCS, SOS, PM, APC */\n-            vt->state = TTY_STATE_OSC;\n-        } else if (ch == '7') {\n-            vt100_save_cursor(vt);\n-            vt->state = TTY_STATE_NORM;\n-        } else if (ch == '8') {\n-            vt100_restore_cursor(vt);\n-            vt->state = TTY_STATE_NORM;\n-        } else {\n-            vt->state = TTY_STATE_NORM;\n-        }\n-        break;\n-    case TTY_STATE_CSI: /* handle escape sequence parameters */\n-        if (ch >= '0' && ch <= '9') {\n-            if (vt->nb_esc_params < MAX_ESC_PARAMS) {\n-                int *param = &vt->esc_params[vt->nb_esc_params];\n-                int digit = (ch - '0');\n-\n-                *param = (*param <= (INT_MAX - digit) / 10) ?\n-                         *param * 10 + digit : INT_MAX;\n-            }\n-        } else {\n-            if (vt->nb_esc_params < MAX_ESC_PARAMS)\n-                vt->nb_esc_params++;\n-            if (ch == ';' || ch == '?') {\n-                break;\n-            }\n-            trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],\n-                                      ch, vt->nb_esc_params);\n-            vt->state = TTY_STATE_NORM;\n-            switch(ch) {\n-            case 'A':\n-                /* move cursor up */\n-                if (vt->esc_params[0] == 0) {\n-                    vt->esc_params[0] = 1;\n-                }\n-                vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);\n-                break;\n-            case 'B':\n-                /* move cursor down */\n-                if (vt->esc_params[0] == 0) {\n-                    vt->esc_params[0] = 1;\n-                }\n-                vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);\n-                break;\n-            case 'C':\n-                /* move cursor right */\n-                if (vt->esc_params[0] == 0) {\n-                    vt->esc_params[0] = 1;\n-                }\n-                vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);\n-                break;\n-            case 'D':\n-                /* move cursor left */\n-                if (vt->esc_params[0] == 0) {\n-                    vt->esc_params[0] = 1;\n-                }\n-                vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);\n-                break;\n-            case 'G':\n-                /* move cursor to column */\n-                vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);\n-                break;\n-            case 'f':\n-            case 'H':\n-                /* move cursor to row, column */\n-                vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);\n-                break;\n-            case 'J':\n-                switch (vt->esc_params[0]) {\n-                case 0:\n-                    /* clear to end of screen */\n-                    for (y = vt->y; y < vt->height; y++) {\n-                        for (x = 0; x < vt->width; x++) {\n-                            if (y == vt->y && x < vt->x) {\n-                                continue;\n-                            }\n-                            vt100_clear_xy(vt, x, y);\n-                        }\n-                    }\n-                    break;\n-                case 1:\n-                    /* clear from beginning of screen */\n-                    for (y = 0; y <= vt->y; y++) {\n-                        for (x = 0; x < vt->width; x++) {\n-                            if (y == vt->y && x > vt->x) {\n-                                break;\n-                            }\n-                            vt100_clear_xy(vt, x, y);\n-                        }\n-                    }\n-                    break;\n-                case 2:\n-                    /* clear entire screen */\n-                    for (y = 0; y < vt->height; y++) {\n-                        for (x = 0; x < vt->width; x++) {\n-                            vt100_clear_xy(vt, x, y);\n-                        }\n-                    }\n-                    break;\n-                }\n-                break;\n-            case 'K':\n-                switch (vt->esc_params[0]) {\n-                case 0:\n-                    /* clear to eol */\n-                    for(x = vt->x; x < vt->width; x++) {\n-                        vt100_clear_xy(vt, x, vt->y);\n-                    }\n-                    break;\n-                case 1:\n-                    /* clear from beginning of line */\n-                    for (x = 0; x <= vt->x && x < vt->width; x++) {\n-                        vt100_clear_xy(vt, x, vt->y);\n-                    }\n-                    break;\n-                case 2:\n-                    /* clear entire line */\n-                    for(x = 0; x < vt->width; x++) {\n-                        vt100_clear_xy(vt, x, vt->y);\n-                    }\n-                    break;\n-                }\n-                break;\n-            case 'P':\n-                vt100_csi_P(vt, vt->esc_params[0]);\n-                break;\n-            case 'm':\n-                vt100_handle_escape(vt);\n-                break;\n-            case 'n':\n-                switch (vt->esc_params[0]) {\n-                case 5:\n-                    /* report console status (always succeed)*/\n-                    vt100_write(vt, \"\\033[0n\", 4);\n-                    break;\n-                case 6:\n-                    /* report cursor position */\n-                    response = g_strdup_printf(\"\\033[%d;%dR\",\n-                                               vt->y + 1, vt->x + 1);\n-                    vt100_write(vt, response, strlen(response));\n-                    break;\n-                }\n-                break;\n-            case 's':\n-                vt100_save_cursor(vt);\n-                break;\n-            case 'u':\n-                vt100_restore_cursor(vt);\n-                break;\n-            case '@':\n-                vt100_csi_at(vt, vt->esc_params[0]);\n-                break;\n-            default:\n-                trace_console_putchar_unhandled(ch);\n-                break;\n-            }\n-            break;\n-        }\n-        break;\n-    case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */\n-        if (ch == '\\a') {\n-            /* BEL terminates OSC */\n-            vt->state = TTY_STATE_NORM;\n-        } else if (ch == 27) {\n-            /* ESC might start ST (ESC \\) */\n-            vt->state = TTY_STATE_ESC;\n-        }\n-        /* All other bytes are silently consumed */\n-        break;\n-    case TTY_STATE_G0: /* set character sets */\n-    case TTY_STATE_G1: /* set character sets */\n-        switch (ch) {\n-        case 'B':\n-            /* Latin-1 map */\n-            break;\n-        }\n-        vt->state = TTY_STATE_NORM;\n-        break;\n-    }\n-}\n-\n #define TYPE_CHARDEV_VC \"chardev-vc\"\n DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,\n                          TYPE_CHARDEV_VC)\n \n-static size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len)\n-{\n-    int i;\n-\n-    vt->update_x0 = vt->width * FONT_WIDTH;\n-    vt->update_y0 = vt->height * FONT_HEIGHT;\n-    vt->update_x1 = 0;\n-    vt->update_y1 = 0;\n-    vt100_show_cursor(vt, 0);\n-    for(i = 0; i < len; i++) {\n-        vt100_putchar(vt, buf[i]);\n-    }\n-    vt100_show_cursor(vt, 1);\n-    if (vt->update_x0 < vt->update_x1) {\n-        vt100_image_update(vt, vt->update_x0, vt->update_y0,\n-                           vt->update_x1 - vt->update_x0,\n-                           vt->update_y1 - vt->update_y0);\n-    }\n-    return len;\n-}\n-\n static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)\n {\n     VCChardev *drv = VC_CHARDEV(chr);\n@@ -1093,30 +111,6 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)\n     return vt100_input(&s->vt, buf, len);\n }\n \n-void vt100_update_cursor(void)\n-{\n-    QemuVT100 *vt;\n-\n-    cursor_visible_phase = !cursor_visible_phase;\n-\n-    if (QTAILQ_EMPTY(&vt100s)) {\n-        return;\n-    }\n-\n-    QTAILQ_FOREACH(vt, &vt100s, list) {\n-        vt100_refresh(vt);\n-    }\n-\n-    timer_mod(cursor_timer,\n-        qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);\n-}\n-\n-static void\n-cursor_timer_cb(void *opaque)\n-{\n-    vt100_update_cursor();\n-}\n-\n static void text_console_invalidate(void *opaque)\n {\n     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);\n@@ -1127,13 +121,6 @@ static void text_console_invalidate(void *opaque)\n     vt100_refresh(&s->vt);\n }\n \n-static void vt100_fini(QemuVT100 *vt)\n-{\n-    QTAILQ_REMOVE(&vt100s, vt, list);\n-    fifo8_destroy(&vt->out_fifo);\n-    g_free(vt->cells);\n-}\n-\n static void\n qemu_text_console_finalize(Object *obj)\n {\n@@ -1212,25 +199,6 @@ static void text_console_out_flush(QemuVT100 *vt)\n     qemu_text_console_out_flush(console);\n }\n \n-static void vt100_init(QemuVT100 *vt,\n-                       pixman_image_t *image,\n-                       void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),\n-                       void (*out_flush)(QemuVT100 *vt))\n-{\n-    if (!cursor_timer) {\n-        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);\n-    }\n-\n-    QTAILQ_INSERT_HEAD(&vt100s, vt, list);\n-    fifo8_create(&vt->out_fifo, 16);\n-    vt->total_height = DEFAULT_BACKSCROLL;\n-    vt->image_update = image_update;\n-    vt->out_flush = out_flush;\n-    /* set current text attributes to default */\n-    vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n-    vt100_set_image(vt, image);\n-}\n-\n static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)\n {\n     ChardevVC *vc = backend->u.vc.data;\ndiff --git a/ui/console.c b/ui/console.c\nindex c997e8df572..7ffea2776ef 100644\n--- a/ui/console.c\n+++ b/ui/console.c\n@@ -39,6 +39,8 @@\n #include \"system/memory.h\"\n #include \"qom/object.h\"\n #include \"qemu/memfd.h\"\n+#include \"ui/vt100.h\"\n+#include \"vgafont.h\"\n \n #include \"console-priv.h\"\n \ndiff --git a/ui/vt100.c b/ui/vt100.c\nnew file mode 100644\nindex 00000000000..ef97eb7ba67\n--- /dev/null\n+++ b/ui/vt100.c\n@@ -0,0 +1,986 @@\n+/*\n+ * SPDX-License-Identifier: MIT\n+ * QEMU vt100\n+ */\n+#include \"qemu/osdep.h\"\n+#include \"qemu/timer.h\"\n+#include \"cp437.h\"\n+#include \"vgafont.h\"\n+#include \"vt100.h\"\n+\n+#include \"trace.h\"\n+\n+#define DEFAULT_BACKSCROLL 512\n+#define CONSOLE_CURSOR_PERIOD 500\n+\n+static const pixman_color_t color_table_rgb[2][8] = {\n+    {   /* dark */\n+        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,\n+        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa),  /* blue */\n+        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00),  /* green */\n+        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa),  /* cyan */\n+        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00),  /* red */\n+        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa),  /* magenta */\n+        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00),  /* yellow */\n+        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR_GRAY,\n+    },\n+    {   /* bright */\n+        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,\n+        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff),  /* blue */\n+        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00),  /* green */\n+        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff),  /* cyan */\n+        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00),  /* red */\n+        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff),  /* magenta */\n+        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00),  /* yellow */\n+        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff),  /* white */\n+    }\n+};\n+\n+static bool cursor_visible_phase;\n+static QEMUTimer *cursor_timer;\n+static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =\n+    QTAILQ_HEAD_INITIALIZER(vt100s);\n+\n+static void image_fill_rect(pixman_image_t *image, int posx, int posy,\n+                            int width, int height, pixman_color_t color)\n+{\n+    pixman_rectangle16_t rect = {\n+        .x = posx, .y = posy, .width = width, .height = height\n+    };\n+\n+    pixman_image_fill_rectangles(PIXMAN_OP_SRC, image,\n+                                 &color, 1, &rect);\n+}\n+\n+/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */\n+static void image_bitblt(pixman_image_t *image,\n+                         int xs, int ys, int xd, int yd, int w, int h)\n+{\n+    pixman_image_composite(PIXMAN_OP_SRC,\n+                           image, NULL, image,\n+                           xs, ys, 0, 0, xd, yd, w, h);\n+}\n+\n+static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,\n+                            TextAttributes *t_attrib)\n+{\n+    static pixman_image_t *glyphs[256];\n+    pixman_color_t fgcol, bgcol;\n+\n+    assert(vt->image);\n+    if (t_attrib->invers) {\n+        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];\n+        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];\n+    } else {\n+        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];\n+        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];\n+    }\n+\n+    if (!glyphs[ch]) {\n+        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);\n+    }\n+    qemu_pixman_glyph_render(glyphs[ch], vt->image,\n+                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);\n+}\n+\n+static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)\n+{\n+    if (vt->update_x0 > x * FONT_WIDTH) {\n+        vt->update_x0 = x * FONT_WIDTH;\n+    }\n+    if (vt->update_y0 > y * FONT_HEIGHT) {\n+        vt->update_y0 = y * FONT_HEIGHT;\n+    }\n+    if (vt->update_x1 < (x + 1) * FONT_WIDTH) {\n+        vt->update_x1 = (x + 1) * FONT_WIDTH;\n+    }\n+    if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {\n+        vt->update_y1 = (y + 1) * FONT_HEIGHT;\n+    }\n+}\n+\n+static void vt100_show_cursor(QemuVT100 *vt, int show)\n+{\n+    TextCell *c;\n+    int y, y1;\n+    int x = vt->x;\n+\n+    vt->cursor_invalidate = 1;\n+\n+    if (x >= vt->width) {\n+        x = vt->width - 1;\n+    }\n+    y1 = (vt->y_base + vt->y) % vt->total_height;\n+    y = y1 - vt->y_displayed;\n+    if (y < 0) {\n+        y += vt->total_height;\n+    }\n+    if (y < vt->height) {\n+        c = &vt->cells[y1 * vt->width + x];\n+        if (show && cursor_visible_phase) {\n+            TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n+            t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */\n+            vt100_putcharxy(vt, x, y, c->ch, &t_attrib);\n+        } else {\n+            vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));\n+        }\n+        vt100_invalidate_xy(vt, x, y);\n+    }\n+}\n+\n+static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)\n+{\n+    vt->image_update(vt, x, y, width, height);\n+}\n+\n+void vt100_refresh(QemuVT100 *vt)\n+{\n+    TextCell *c;\n+    int x, y, y1;\n+    int w = pixman_image_get_width(vt->image);\n+    int h = pixman_image_get_height(vt->image);\n+\n+    vt->text_x[0] = 0;\n+    vt->text_y[0] = 0;\n+    vt->text_x[1] = vt->width - 1;\n+    vt->text_y[1] = vt->height - 1;\n+    vt->cursor_invalidate = 1;\n+\n+    image_fill_rect(vt->image, 0, 0, w, h,\n+                    color_table_rgb[0][QEMU_COLOR_BLACK]);\n+    y1 = vt->y_displayed;\n+    for (y = 0; y < vt->height; y++) {\n+        c = vt->cells + y1 * vt->width;\n+        for (x = 0; x < vt->width; x++) {\n+            vt100_putcharxy(vt, x, y, c->ch,\n+                            &(c->t_attrib));\n+            c++;\n+        }\n+        if (++y1 == vt->total_height) {\n+            y1 = 0;\n+        }\n+    }\n+    vt100_show_cursor(vt, 1);\n+    vt100_image_update(vt, 0, 0, w, h);\n+}\n+\n+static void vt100_scroll(QemuVT100 *vt, int ydelta)\n+{\n+    int i, y1;\n+\n+    if (ydelta > 0) {\n+        for (i = 0; i < ydelta; i++) {\n+            if (vt->y_displayed == vt->y_base) {\n+                break;\n+            }\n+            if (++vt->y_displayed == vt->total_height) {\n+                vt->y_displayed = 0;\n+            }\n+        }\n+    } else {\n+        ydelta = -ydelta;\n+        i = vt->backscroll_height;\n+        if (i > vt->total_height - vt->height) {\n+            i = vt->total_height - vt->height;\n+        }\n+        y1 = vt->y_base - i;\n+        if (y1 < 0) {\n+            y1 += vt->total_height;\n+        }\n+        for (i = 0; i < ydelta; i++) {\n+            if (vt->y_displayed == y1) {\n+                break;\n+            }\n+            if (--vt->y_displayed < 0) {\n+                vt->y_displayed = vt->total_height - 1;\n+            }\n+        }\n+    }\n+    vt100_refresh(vt);\n+}\n+\n+static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)\n+{\n+    uint32_t num_free;\n+\n+    num_free = fifo8_num_free(&vt->out_fifo);\n+    fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));\n+    vt->out_flush(vt);\n+}\n+\n+void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)\n+{\n+    TextCell *cells, *c, *c1;\n+    int w1, x, y, last_width, w, h;\n+\n+    vt->image = image;\n+    w = pixman_image_get_width(vt->image) / FONT_WIDTH;\n+    h = pixman_image_get_height(vt->image) / FONT_HEIGHT;\n+    if (w == vt->width && h == vt->height) {\n+        return;\n+    }\n+\n+    last_width = vt->width;\n+    vt->width = w;\n+    vt->height = h;\n+\n+    w1 = MIN(vt->width, last_width);\n+\n+    cells = g_new(TextCell, vt->width * vt->total_height + 1);\n+    for (y = 0; y < vt->total_height; y++) {\n+        c = &cells[y * vt->width];\n+        if (w1 > 0) {\n+            c1 = &vt->cells[y * last_width];\n+            for (x = 0; x < w1; x++) {\n+                *c++ = *c1++;\n+            }\n+        }\n+        for (x = w1; x < vt->width; x++) {\n+            c->ch = ' ';\n+            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n+            c++;\n+        }\n+    }\n+    g_free(vt->cells);\n+    vt->cells = cells;\n+}\n+\n+static void vt100_put_lf(QemuVT100 *vt)\n+{\n+    TextCell *c;\n+    int x, y1;\n+\n+    vt->y++;\n+    if (vt->y >= vt->height) {\n+        vt->y = vt->height - 1;\n+\n+        if (vt->y_displayed == vt->y_base) {\n+            if (++vt->y_displayed == vt->total_height) {\n+                vt->y_displayed = 0;\n+            }\n+        }\n+        if (++vt->y_base == vt->total_height) {\n+            vt->y_base = 0;\n+        }\n+        if (vt->backscroll_height < vt->total_height) {\n+            vt->backscroll_height++;\n+        }\n+        y1 = (vt->y_base + vt->height - 1) % vt->total_height;\n+        c = &vt->cells[y1 * vt->width];\n+        for (x = 0; x < vt->width; x++) {\n+            c->ch = ' ';\n+            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n+            c++;\n+        }\n+        if (vt->y_displayed == vt->y_base) {\n+            vt->text_x[0] = 0;\n+            vt->text_y[0] = 0;\n+            vt->text_x[1] = vt->width - 1;\n+            vt->text_y[1] = vt->height - 1;\n+\n+            image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,\n+                         vt->width * FONT_WIDTH,\n+                         (vt->height - 1) * FONT_HEIGHT);\n+            image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,\n+                            vt->width * FONT_WIDTH, FONT_HEIGHT,\n+                            color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);\n+            vt->update_x0 = 0;\n+            vt->update_y0 = 0;\n+            vt->update_x1 = vt->width * FONT_WIDTH;\n+            vt->update_y1 = vt->height * FONT_HEIGHT;\n+        }\n+    }\n+}\n+\n+/*\n+ * Set console attributes depending on the current escape codes.\n+ * NOTE: I know this code is not very efficient (checking every color for it\n+ * self) but it is more readable and better maintainable.\n+ */\n+static void vt100_handle_escape(QemuVT100 *vt)\n+{\n+    int i;\n+\n+    for (i = 0; i < vt->nb_esc_params; i++) {\n+        switch (vt->esc_params[i]) {\n+        case 0: /* reset all console attributes to default */\n+            vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n+            break;\n+        case 1:\n+            vt->t_attrib.bold = 1;\n+            break;\n+        case 4:\n+            vt->t_attrib.uline = 1;\n+            break;\n+        case 5:\n+            vt->t_attrib.blink = 1;\n+            break;\n+        case 7:\n+            vt->t_attrib.invers = 1;\n+            break;\n+        case 8:\n+            vt->t_attrib.unvisible = 1;\n+            break;\n+        case 22:\n+            vt->t_attrib.bold = 0;\n+            break;\n+        case 24:\n+            vt->t_attrib.uline = 0;\n+            break;\n+        case 25:\n+            vt->t_attrib.blink = 0;\n+            break;\n+        case 27:\n+            vt->t_attrib.invers = 0;\n+            break;\n+        case 28:\n+            vt->t_attrib.unvisible = 0;\n+            break;\n+        /* set foreground color */\n+        case 30:\n+            vt->t_attrib.fgcol = QEMU_COLOR_BLACK;\n+            break;\n+        case 31:\n+            vt->t_attrib.fgcol = QEMU_COLOR_RED;\n+            break;\n+        case 32:\n+            vt->t_attrib.fgcol = QEMU_COLOR_GREEN;\n+            break;\n+        case 33:\n+            vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;\n+            break;\n+        case 34:\n+            vt->t_attrib.fgcol = QEMU_COLOR_BLUE;\n+            break;\n+        case 35:\n+            vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;\n+            break;\n+        case 36:\n+            vt->t_attrib.fgcol = QEMU_COLOR_CYAN;\n+            break;\n+        case 37:\n+            vt->t_attrib.fgcol = QEMU_COLOR_WHITE;\n+            break;\n+        /* set background color */\n+        case 40:\n+            vt->t_attrib.bgcol = QEMU_COLOR_BLACK;\n+            break;\n+        case 41:\n+            vt->t_attrib.bgcol = QEMU_COLOR_RED;\n+            break;\n+        case 42:\n+            vt->t_attrib.bgcol = QEMU_COLOR_GREEN;\n+            break;\n+        case 43:\n+            vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;\n+            break;\n+        case 44:\n+            vt->t_attrib.bgcol = QEMU_COLOR_BLUE;\n+            break;\n+        case 45:\n+            vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;\n+            break;\n+        case 46:\n+            vt->t_attrib.bgcol = QEMU_COLOR_CYAN;\n+            break;\n+        case 47:\n+            vt->t_attrib.bgcol = QEMU_COLOR_WHITE;\n+            break;\n+        }\n+    }\n+}\n+\n+static void vt100_update_xy(QemuVT100 *vt, int x, int y)\n+{\n+    TextCell *c;\n+    int y1, y2;\n+\n+    vt->text_x[0] = MIN(vt->text_x[0], x);\n+    vt->text_x[1] = MAX(vt->text_x[1], x);\n+    vt->text_y[0] = MIN(vt->text_y[0], y);\n+    vt->text_y[1] = MAX(vt->text_y[1], y);\n+\n+    y1 = (vt->y_base + y) % vt->total_height;\n+    y2 = y1 - vt->y_displayed;\n+    if (y2 < 0) {\n+        y2 += vt->total_height;\n+    }\n+    if (y2 < vt->height) {\n+        if (x >= vt->width) {\n+            x = vt->width - 1;\n+        }\n+        c = &vt->cells[y1 * vt->width + x];\n+        vt100_putcharxy(vt, x, y2, c->ch,\n+                      &(c->t_attrib));\n+        vt100_invalidate_xy(vt, x, y2);\n+    }\n+}\n+\n+static void vt100_clear_xy(QemuVT100 *vt, int x, int y)\n+{\n+    int y1 = (vt->y_base + y) % vt->total_height;\n+    if (x >= vt->width) {\n+        x = vt->width - 1;\n+    }\n+    TextCell *c = &vt->cells[y1 * vt->width + x];\n+    c->ch = ' ';\n+    c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n+    vt100_update_xy(vt, x, y);\n+}\n+\n+/*\n+ * UTF-8 DFA decoder by Bjoern Hoehrmann.\n+ * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n+ * See https://github.com/polijan/utf8_decode for details.\n+ *\n+ * SPDX-License-Identifier: MIT\n+ */\n+#define BH_UTF8_ACCEPT 0\n+#define BH_UTF8_REJECT 12\n+\n+static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)\n+{\n+    static const uint8_t utf8d[] = {\n+        /* character class lookup */\n+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n+        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n+        8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,\n+        10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,\n+\n+        /* state transition lookup */\n+        0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,\n+        12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,\n+        12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,\n+        12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,\n+        12,36,12,12,12,12,12,12,12,12,12,12,\n+    };\n+    uint32_t type = utf8d[byte];\n+\n+    *codep = (*state != BH_UTF8_ACCEPT) ?\n+        (byte & 0x3fu) | (*codep << 6) :\n+        (0xffu >> type) & (byte);\n+\n+    *state = utf8d[256 + *state + type];\n+    return *state;\n+}\n+\n+static void vt100_put_one(QemuVT100 *vt, int ch)\n+{\n+    TextCell *c;\n+    int y1;\n+    if (vt->x >= vt->width) {\n+        /* line wrap */\n+        vt->x = 0;\n+        vt100_put_lf(vt);\n+    }\n+    y1 = (vt->y_base + vt->y) % vt->total_height;\n+    c = &vt->cells[y1 * vt->width + vt->x];\n+    c->ch = ch;\n+    c->t_attrib = vt->t_attrib;\n+    vt100_update_xy(vt, vt->x, vt->y);\n+    vt->x++;\n+}\n+\n+/* set cursor, checking bounds */\n+static void vt100_set_cursor(QemuVT100 *vt, int x, int y)\n+{\n+    if (x < 0) {\n+        x = 0;\n+    }\n+    if (y < 0) {\n+        y = 0;\n+    }\n+    if (y >= vt->height) {\n+        y = vt->height - 1;\n+    }\n+    if (x >= vt->width) {\n+        x = vt->width - 1;\n+    }\n+\n+    vt->x = x;\n+    vt->y = y;\n+}\n+\n+/**\n+ * vt100_csi_P() - (DCH) deletes one or more characters from the cursor\n+ * position to the right. As characters are deleted, the remaining\n+ * characters between the cursor and right margin move to the\n+ * left. Character attributes move with the characters.\n+ */\n+static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)\n+{\n+    TextCell *c1, *c2;\n+    unsigned int x1, x2, y;\n+    unsigned int end, len;\n+\n+    if (!nr) {\n+        nr = 1;\n+    }\n+    if (nr > vt->width - vt->x) {\n+        nr = vt->width - vt->x;\n+        if (!nr) {\n+            return;\n+        }\n+    }\n+\n+    x1 = vt->x;\n+    x2 = vt->x + nr;\n+    len = vt->width - x2;\n+    if (len) {\n+        y = (vt->y_base + vt->y) % vt->total_height;\n+        c1 = &vt->cells[y * vt->width + x1];\n+        c2 = &vt->cells[y * vt->width + x2];\n+        memmove(c1, c2, len * sizeof(*c1));\n+        for (end = x1 + len; x1 < end; x1++) {\n+            vt100_update_xy(vt, x1, vt->y);\n+        }\n+    }\n+    /* Clear the rest */\n+    for (; x1 < vt->width; x1++) {\n+        vt100_clear_xy(vt, x1, vt->y);\n+    }\n+}\n+\n+/**\n+ * vt100_csi_at() - (ICH) inserts `nr` blank characters with the default\n+ * character attribute. The cursor remains at the beginning of the\n+ * blank characters. Text between the cursor and right margin moves to\n+ * the right. Characters scrolled past the right margin are lost.\n+ */\n+static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)\n+{\n+    TextCell *c1, *c2;\n+    unsigned int x1, x2, y;\n+    unsigned int end, len;\n+\n+    if (!nr) {\n+        nr = 1;\n+    }\n+    if (nr > vt->width - vt->x) {\n+        nr = vt->width - vt->x;\n+        if (!nr) {\n+            return;\n+        }\n+    }\n+\n+    x1 = vt->x + nr;\n+    x2 = vt->x;\n+    len = vt->width - x1;\n+    if (len) {\n+        y = (vt->y_base + vt->y) % vt->total_height;\n+        c1 = &vt->cells[y * vt->width + x1];\n+        c2 = &vt->cells[y * vt->width + x2];\n+        memmove(c1, c2, len * sizeof(*c1));\n+        for (end = x1 + len; x1 < end; x1++) {\n+            vt100_update_xy(vt, x1, vt->y);\n+        }\n+    }\n+    /* Insert blanks */\n+    for (x1 = vt->x; x1 < vt->x + nr; x1++) {\n+        vt100_clear_xy(vt, x1, vt->y);\n+    }\n+}\n+\n+/**\n+ * vt100_save_cursor() - saves cursor position and character attributes.\n+ */\n+static void vt100_save_cursor(QemuVT100 *vt)\n+{\n+    vt->x_saved = vt->x;\n+    vt->y_saved = vt->y;\n+    vt->t_attrib_saved = vt->t_attrib;\n+}\n+\n+/**\n+ * vt100_restore_cursor() - restores cursor position and character\n+ * attributes from saved state.\n+ */\n+static void vt100_restore_cursor(QemuVT100 *vt)\n+{\n+    vt->x = vt->x_saved;\n+    vt->y = vt->y_saved;\n+    vt->t_attrib = vt->t_attrib_saved;\n+}\n+\n+static void vt100_putchar(QemuVT100 *vt, int ch)\n+{\n+    int i;\n+    int x, y;\n+    g_autofree char *response = NULL;\n+\n+    switch (vt->state) {\n+    case TTY_STATE_NORM:\n+        /* Feed byte through the UTF-8 DFA decoder */\n+        if (ch >= 0x80) {\n+            switch (bh_utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {\n+            case BH_UTF8_ACCEPT:\n+                vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));\n+                break;\n+            case BH_UTF8_REJECT:\n+                /* Reset state so the decoder can resync */\n+                vt->utf8_state = BH_UTF8_ACCEPT;\n+                break;\n+            default:\n+                /* Need more bytes */\n+                break;\n+            }\n+            break;\n+        }\n+        /* ASCII byte: abort any pending UTF-8 sequence */\n+        vt->utf8_state = BH_UTF8_ACCEPT;\n+        switch (ch) {\n+        case '\\r':  /* carriage return */\n+            vt->x = 0;\n+            break;\n+        case '\\n':  /* newline */\n+            vt100_put_lf(vt);\n+            break;\n+        case '\\b':  /* backspace */\n+            if (vt->x > 0) {\n+                vt->x--;\n+            }\n+            break;\n+        case '\\t':  /* tabspace */\n+            if (vt->x + (8 - (vt->x % 8)) > vt->width) {\n+                vt->x = 0;\n+                vt100_put_lf(vt);\n+            } else {\n+                vt->x = vt->x + (8 - (vt->x % 8));\n+            }\n+            break;\n+        case '\\a':  /* alert aka. bell */\n+            /* TODO: has to be implemented */\n+            break;\n+        case 14:\n+            /* SO (shift out), character set 1 (ignored) */\n+            break;\n+        case 15:\n+            /* SI (shift in), character set 0 (ignored) */\n+            break;\n+        case 27:    /* esc (introducing an escape sequence) */\n+            vt->state = TTY_STATE_ESC;\n+            break;\n+        default:\n+            vt100_put_one(vt, ch);\n+            break;\n+        }\n+        break;\n+    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */\n+        if (ch == '[') {\n+            for (i = 0; i < MAX_ESC_PARAMS; i++) {\n+                vt->esc_params[i] = 0;\n+            }\n+            vt->nb_esc_params = 0;\n+            vt->state = TTY_STATE_CSI;\n+        } else if (ch == '(') {\n+            vt->state = TTY_STATE_G0;\n+        } else if (ch == ')') {\n+            vt->state = TTY_STATE_G1;\n+        } else if (ch == ']' || ch == 'P' || ch == 'X'\n+                   || ch == '^' || ch == '_') {\n+            /* String sequences: OSC, DCS, SOS, PM, APC */\n+            vt->state = TTY_STATE_OSC;\n+        } else if (ch == '7') {\n+            vt100_save_cursor(vt);\n+            vt->state = TTY_STATE_NORM;\n+        } else if (ch == '8') {\n+            vt100_restore_cursor(vt);\n+            vt->state = TTY_STATE_NORM;\n+        } else {\n+            vt->state = TTY_STATE_NORM;\n+        }\n+        break;\n+    case TTY_STATE_CSI: /* handle escape sequence parameters */\n+        if (ch >= '0' && ch <= '9') {\n+            if (vt->nb_esc_params < MAX_ESC_PARAMS) {\n+                int *param = &vt->esc_params[vt->nb_esc_params];\n+                int digit = (ch - '0');\n+\n+                *param = (*param <= (INT_MAX - digit) / 10) ?\n+                         *param * 10 + digit : INT_MAX;\n+            }\n+        } else {\n+            if (vt->nb_esc_params < MAX_ESC_PARAMS) {\n+                vt->nb_esc_params++;\n+            }\n+            if (ch == ';' || ch == '?') {\n+                break;\n+            }\n+            trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],\n+                                      ch, vt->nb_esc_params);\n+            vt->state = TTY_STATE_NORM;\n+            switch (ch) {\n+            case 'A':\n+                /* move cursor up */\n+                if (vt->esc_params[0] == 0) {\n+                    vt->esc_params[0] = 1;\n+                }\n+                vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);\n+                break;\n+            case 'B':\n+                /* move cursor down */\n+                if (vt->esc_params[0] == 0) {\n+                    vt->esc_params[0] = 1;\n+                }\n+                vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);\n+                break;\n+            case 'C':\n+                /* move cursor right */\n+                if (vt->esc_params[0] == 0) {\n+                    vt->esc_params[0] = 1;\n+                }\n+                vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);\n+                break;\n+            case 'D':\n+                /* move cursor left */\n+                if (vt->esc_params[0] == 0) {\n+                    vt->esc_params[0] = 1;\n+                }\n+                vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);\n+                break;\n+            case 'G':\n+                /* move cursor to column */\n+                vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);\n+                break;\n+            case 'f':\n+            case 'H':\n+                /* move cursor to row, column */\n+                vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);\n+                break;\n+            case 'J':\n+                switch (vt->esc_params[0]) {\n+                case 0:\n+                    /* clear to end of screen */\n+                    for (y = vt->y; y < vt->height; y++) {\n+                        for (x = 0; x < vt->width; x++) {\n+                            if (y == vt->y && x < vt->x) {\n+                                continue;\n+                            }\n+                            vt100_clear_xy(vt, x, y);\n+                        }\n+                    }\n+                    break;\n+                case 1:\n+                    /* clear from beginning of screen */\n+                    for (y = 0; y <= vt->y; y++) {\n+                        for (x = 0; x < vt->width; x++) {\n+                            if (y == vt->y && x > vt->x) {\n+                                break;\n+                            }\n+                            vt100_clear_xy(vt, x, y);\n+                        }\n+                    }\n+                    break;\n+                case 2:\n+                    /* clear entire screen */\n+                    for (y = 0; y < vt->height; y++) {\n+                        for (x = 0; x < vt->width; x++) {\n+                            vt100_clear_xy(vt, x, y);\n+                        }\n+                    }\n+                    break;\n+                }\n+                break;\n+            case 'K':\n+                switch (vt->esc_params[0]) {\n+                case 0:\n+                    /* clear to eol */\n+                    for (x = vt->x; x < vt->width; x++) {\n+                        vt100_clear_xy(vt, x, vt->y);\n+                    }\n+                    break;\n+                case 1:\n+                    /* clear from beginning of line */\n+                    for (x = 0; x <= vt->x && x < vt->width; x++) {\n+                        vt100_clear_xy(vt, x, vt->y);\n+                    }\n+                    break;\n+                case 2:\n+                    /* clear entire line */\n+                    for (x = 0; x < vt->width; x++) {\n+                        vt100_clear_xy(vt, x, vt->y);\n+                    }\n+                    break;\n+                }\n+                break;\n+            case 'P':\n+                vt100_csi_P(vt, vt->esc_params[0]);\n+                break;\n+            case 'm':\n+                vt100_handle_escape(vt);\n+                break;\n+            case 'n':\n+                switch (vt->esc_params[0]) {\n+                case 5:\n+                    /* report console status (always succeed)*/\n+                    vt100_write(vt, \"\\033[0n\", 4);\n+                    break;\n+                case 6:\n+                    /* report cursor position */\n+                    response = g_strdup_printf(\"\\033[%d;%dR\",\n+                                               vt->y + 1, vt->x + 1);\n+                    vt100_write(vt, response, strlen(response));\n+                    break;\n+                }\n+                break;\n+            case 's':\n+                vt100_save_cursor(vt);\n+                break;\n+            case 'u':\n+                vt100_restore_cursor(vt);\n+                break;\n+            case '@':\n+                vt100_csi_at(vt, vt->esc_params[0]);\n+                break;\n+            default:\n+                trace_console_putchar_unhandled(ch);\n+                break;\n+            }\n+            break;\n+        }\n+        break;\n+    case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */\n+        if (ch == '\\a') {\n+            /* BEL terminates OSC */\n+            vt->state = TTY_STATE_NORM;\n+        } else if (ch == 27) {\n+            /* ESC might start ST (ESC \\) */\n+            vt->state = TTY_STATE_ESC;\n+        }\n+        /* All other bytes are silently consumed */\n+        break;\n+    case TTY_STATE_G0: /* set character sets */\n+    case TTY_STATE_G1: /* set character sets */\n+        switch (ch) {\n+        case 'B':\n+            /* Latin-1 map */\n+            break;\n+        }\n+        vt->state = TTY_STATE_NORM;\n+        break;\n+    }\n+\n+}\n+\n+size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len)\n+{\n+    int i;\n+\n+    vt->update_x0 = vt->width * FONT_WIDTH;\n+    vt->update_y0 = vt->height * FONT_HEIGHT;\n+    vt->update_x1 = 0;\n+    vt->update_y1 = 0;\n+    vt100_show_cursor(vt, 0);\n+    for (i = 0; i < len; i++) {\n+        vt100_putchar(vt, buf[i]);\n+    }\n+    vt100_show_cursor(vt, 1);\n+    if (vt->update_x0 < vt->update_x1) {\n+        vt100_image_update(vt, vt->update_x0, vt->update_y0,\n+                           vt->update_x1 - vt->update_x0,\n+                           vt->update_y1 - vt->update_y0);\n+    }\n+    return len;\n+}\n+\n+void vt100_keysym(QemuVT100 *vt, int keysym)\n+{\n+    uint8_t buf[16], *q;\n+    int c;\n+\n+    switch (keysym) {\n+    case QEMU_KEY_CTRL_UP:\n+        vt100_scroll(vt, -1);\n+        break;\n+    case QEMU_KEY_CTRL_DOWN:\n+        vt100_scroll(vt, 1);\n+        break;\n+    case QEMU_KEY_CTRL_PAGEUP:\n+        vt100_scroll(vt, -10);\n+        break;\n+    case QEMU_KEY_CTRL_PAGEDOWN:\n+        vt100_scroll(vt, 10);\n+        break;\n+    default:\n+        /* convert the QEMU keysym to VT100 key string */\n+        q = buf;\n+        if (keysym >= 0xe100 && keysym <= 0xe11f) {\n+            *q++ = '\\033';\n+            *q++ = '[';\n+            c = keysym - 0xe100;\n+            if (c >= 10) {\n+                *q++ = '0' + (c / 10);\n+            }\n+            *q++ = '0' + (c % 10);\n+            *q++ = '~';\n+        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {\n+            *q++ = '\\033';\n+            *q++ = '[';\n+            *q++ = keysym & 0xff;\n+        } else if (vt->echo && (keysym == '\\r' || keysym == '\\n')) {\n+            vt100_input(vt, (uint8_t *)\"\\r\", 1);\n+            *q++ = '\\n';\n+        } else {\n+            *q++ = keysym;\n+        }\n+        if (vt->echo) {\n+            vt100_input(vt, buf, q - buf);\n+        }\n+        vt100_write(vt, buf, q - buf);\n+        break;\n+    }\n+}\n+\n+void vt100_update_cursor(void)\n+{\n+    QemuVT100 *vt;\n+\n+    cursor_visible_phase = !cursor_visible_phase;\n+\n+    if (QTAILQ_EMPTY(&vt100s)) {\n+        return;\n+    }\n+\n+    QTAILQ_FOREACH(vt, &vt100s, list) {\n+        vt100_refresh(vt);\n+    }\n+\n+    timer_mod(cursor_timer,\n+        qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);\n+}\n+\n+static void\n+cursor_timer_cb(void *opaque)\n+{\n+    vt100_update_cursor();\n+}\n+\n+void vt100_init(QemuVT100 *vt,\n+                pixman_image_t *image,\n+                void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),\n+                void (*out_flush)(QemuVT100 *vt))\n+{\n+    if (!cursor_timer) {\n+        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);\n+    }\n+\n+    QTAILQ_INSERT_HEAD(&vt100s, vt, list);\n+    fifo8_create(&vt->out_fifo, 16);\n+    vt->total_height = DEFAULT_BACKSCROLL;\n+    vt->image_update = image_update;\n+    vt->out_flush = out_flush;\n+    /* set current text attributes to default */\n+    vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;\n+    vt100_set_image(vt, image);\n+}\n+\n+void vt100_fini(QemuVT100 *vt)\n+{\n+    QTAILQ_REMOVE(&vt100s, vt, list);\n+    fifo8_destroy(&vt->out_fifo);\n+    g_free(vt->cells);\n+}\ndiff --git a/ui/meson.build b/ui/meson.build\nindex 25657af50e7..9ece6f262b6 100644\n--- a/ui/meson.build\n+++ b/ui/meson.build\n@@ -3,6 +3,7 @@ system_ss.add(png)\n system_ss.add(files(\n   'clipboard.c',\n   'console.c',\n+  'cp437.c',\n   'cursor.c',\n   'dmabuf.c',\n   'input-keymap.c',\n@@ -16,8 +17,9 @@ system_ss.add(files(\n   'ui-qmp-cmds.c',\n   'util.c',\n   'vgafont.c',\n+  'vt100.c',\n ))\n-system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))\n+system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))\n if dbus_display\n   system_ss.add(files('dbus-module.c'))\n endif\n","prefixes":["v2","33/67"]}