feat(xteink): expose the panel frame generation over QOM

The wasm front end already tracked panel refreshes; make the counter
unconditional and alias it onto the machine so native front ends can
redraw on refresh instead of on a timer.
This commit is contained in:
2026-07-26 11:18:28 -04:00
parent d613b079ef
commit 4cfbdfbd15
2 changed files with 24 additions and 8 deletions
+22 -8
View File
@@ -11,11 +11,21 @@
#include "hw/irq.h"
#include "hw/display/xteink_x3_eink.h"
/* Frame Counter - Both the wasm front end and the QMP web UI poll this to redraw only on a real
* panel refresh. Only one panel is instantiated per machine, so a single counter suffices. */
static uint32_t xteink_frame_generation;
static void xteink_frame_generation_init(Object *obj)
{
object_property_add_uint32_ptr(obj, "frame-generation",
&xteink_frame_generation,
OBJ_PROP_FLAG_READ);
}
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
static QemuConsole *xteink_wasm_console;
static uint32_t xteink_wasm_generation;
EMSCRIPTEN_KEEPALIVE uintptr_t xteink_wasm_framebuffer(void)
{
@@ -51,17 +61,18 @@ EMSCRIPTEN_KEEPALIVE int xteink_wasm_stride(void)
EMSCRIPTEN_KEEPALIVE uint32_t xteink_wasm_frame_generation(void)
{
return qatomic_read(&xteink_wasm_generation);
return qatomic_read(&xteink_frame_generation);
}
static void xteink_wasm_frame_updated(QemuConsole *console)
static void xteink_frame_updated(QemuConsole *console)
{
xteink_wasm_console = console;
qatomic_inc(&xteink_wasm_generation);
qatomic_inc(&xteink_frame_generation);
}
#else
static void xteink_wasm_frame_updated(QemuConsole *console)
static void xteink_frame_updated(QemuConsole *console)
{
qatomic_inc(&xteink_frame_generation);
}
#endif
@@ -73,7 +84,8 @@ static void xteink_wasm_frame_updated(QemuConsole *console)
#define CMD_LUT_FIRST 0x20
#define CMD_LUT_LAST 0x24
#define BUSY_TIME_NS (5 * SCALE_MS)
/* Mirror a real panel refresh (~1 s): shorter pulses let the completion edge pass before the firmware arms its ISR, costing a 30 s timeout. */
#define BUSY_TIME_NS (1000 * SCALE_MS)
#define XTEINK_X3_LUT_PREFIX_SIZE 13
@@ -220,7 +232,7 @@ static void xteink_x3_eink_render(XteinkX3EinkState *s)
}
}
xteink_wasm_frame_updated(s->console);
xteink_frame_updated(s->console);
dpy_gfx_update(s->console, 0, 0, XTEINK_X3_HEIGHT, XTEINK_X3_WIDTH);
}
@@ -326,6 +338,7 @@ static void xteink_x3_eink_init(Object *obj)
{
XteinkX3EinkState *s = XTEINK_X3_EINK(obj);
xteink_frame_generation_init(obj);
s->console = graphic_console_init(DEVICE(s), 0,
&xteink_x3_eink_graphics_ops, s);
dpy_gfx_replace_surface(s->console,
@@ -378,7 +391,7 @@ static void xteink_x4_eink_render(XteinkX4EinkState *s)
DisplaySurface *surface = qemu_console_surface(s->console);
memset(surface_data(surface), 0xff,
surface_stride(surface) * surface_height(surface));
xteink_wasm_frame_updated(s->console);
xteink_frame_updated(s->console);
dpy_gfx_update(s->console, 0, 0, 480, 800);
}
@@ -412,6 +425,7 @@ static void xteink_x4_eink_init(Object *obj)
{
XteinkX4EinkState *s = XTEINK_X4_EINK(obj);
xteink_frame_generation_init(obj);
s->console = graphic_console_init(DEVICE(s), 0,
&xteink_x4_eink_graphics_ops, s);
dpy_gfx_replace_surface(s->console, qemu_create_displaysurface(480, 800));
+2
View File
@@ -868,6 +868,8 @@ static void xteink_machine_init(MachineState *machine)
DeviceState *panel = ssi_create_peripheral(ms->spi2.bus,
ms->x4 ? TYPE_XTEINK_X4_EINK : TYPE_XTEINK_X3_EINK);
qemu_input_handler_register(panel, &xteink_key_handler);
object_property_add_alias(OBJECT(machine), "frame-generation",
OBJECT(panel), "frame-generation");
qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 21,
qdev_get_gpio_in_named(panel, SSI_GPIO_CS, 0));
qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 4,