Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2222108/?format=api
{ "id": 2222108, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2222108/?format=api", "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=api", "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=api", "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=api", "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" ] }