From 5adc27357eadbee55f4f7177a9eb84fc979eb767 Mon Sep 17 00:00:00 2001 From: Patryk Kubiak Date: Sun, 11 Oct 2026 23:42:05 +0200 Subject: [PATCH 1/2] libdmview: a box that is not seen draws nothing when it changes A changed box was redrawn - with what is seen beneath it, up to an opaque box or the root - even when it was not seen: a screen of a page behind the one shown, its opacity 0. The car HMI's speedometer animated on its hidden home screen, and the media screen in front of it - its blurred cover images - was drawn again for every frame of it. A changed box is skipped when a box around it was not seen when last drawn (that box's own opacity changing redraws it with all inside it), or when it was not seen itself and its OPACITY still gives 0 - so a screen fading in is drawn. Co-Authored-By: Claude Opus 5.5 --- src/exec.c | 24 +++++++++++++++++++++++- tests/fixtures/hidden.dmvs | 10 +++++++++- tests/libdmview_test.c | 8 ++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/exec.c b/src/exec.c index c94d04f..f692d6b 100644 --- a/src/exec.c +++ b/src/exec.c @@ -744,6 +744,28 @@ static void redraw(struct libdmview* v, int32_t target, const rect_t* area) run(v, b->begin * DMV_CODE_WORD, true); } +/* + * A box that changed but is not seen - a screen of a page behind the one + * shown, its opacity 0 - draws nothing, yet redrawing it redraws what is + * seen beneath it (a page's animations ran under the shown page all the + * time). Unseen: a box around it was not seen when last drawn (when that + * one's opacity changes, it is redrawn itself, with all inside it), or it + * was not seen itself and its OPACITY still makes it so. + */ +static bool stays_unseen(struct libdmview* v, const rbox_t* b) +{ + for (int32_t p = b->parent; p != ROOT; p = v->boxes[p].parent) + { + if (v->boxes[p].seen == 0) + return true; + } + if (b->seen != 0) + return false; + const uint8_t* insn = v->code + b->begin * DMV_CODE_WORD; + const uint8_t* next = insn + insn[1]; + return next[0] == DMV_OP_OPACITY && v16(v, next, 0, OPOFF(OPACITY, 0)) <= 0; +} + void exec_draw(struct libdmview* v, rect_t* changed) { rect_t screen = { 0, 0, v->surface_w, v->surface_h }; @@ -766,7 +788,7 @@ void exec_draw(struct libdmview* v, rect_t* changed) rbox_t* b = &v->boxes[i]; if ((b->flags & BOXF_DIRTY) == 0) continue; - if ((b->flags & BOXF_VISIBLE) == 0) + if ((b->flags & BOXF_VISIBLE) == 0 || stays_unseen(v, b)) { b->flags &= (uint8_t)~BOXF_DIRTY; continue; diff --git a/tests/fixtures/hidden.dmvs b/tests/fixtures/hidden.dmvs index 6b88859..b14b00e 100644 --- a/tests/fixtures/hidden.dmvs +++ b/tests/fixtures/hidden.dmvs @@ -1,12 +1,14 @@ ; Two screens one on another, as a page's views: the one below shown, with ; a button; the one on top hidden by its opacity ($top), with a box taking -; no contact over the button - and a button of its own elsewhere. +; no contact over the button - and a button of its own elsewhere. $tick +; changes what the hidden screen shows: a bar's width, a box's place. .view hidden .entry draw .var $top, int, 0 .var $below, int, 0 .var $above, int, 0 +.var $tick, int, 1 draw: FILL #000000 @@ -25,6 +27,12 @@ draw: ON CLICK, press_above FILL #00FF00 END + BOX @bar, 0, 28, 64, 4 + RECT 0, 0, $tick, 4, #FFFFFF + END + BOX @mover, $tick, 24, 4, 4 + FILL #FFFF00 + END END RET diff --git a/tests/libdmview_test.c b/tests/libdmview_test.c index b3722ba..e9500ea 100644 --- a/tests/libdmview_test.c +++ b/tests/libdmview_test.c @@ -742,6 +742,10 @@ DMOD_TEST_STEP(libdmview_hidden_boxes_take_no_contact) return; DMOD_TEST_EXPECT_EQ(libdmview_render(g_view, &g_s32, NULL), 1); + /* What the hidden screen shows changes: nothing is drawn */ + DMOD_TEST_EXPECT_EQ(libdmview_set_int(g_view, "tick", 9), 0); + DMOD_TEST_EXPECT_EQ(libdmview_render(g_view, &g_s32, NULL), 0); + /* The screen on top is not seen: the button beneath it is clicked */ touch(1, 15, 15, 10); touch(0, 0, 0, 20); @@ -755,6 +759,10 @@ DMOD_TEST_STEP(libdmview_hidden_boxes_take_no_contact) /* Shown: it covers the button, its own takes the contact */ DMOD_TEST_EXPECT_EQ(libdmview_set_int(g_view, "top", 255), 0); DMOD_TEST_EXPECT_EQ(libdmview_render(g_view, &g_s32, NULL), 1); + DMOD_TEST_EXPECT_EQ(px(1, 29), 0xFFFFFFFFu); /* The bar as $tick has it now */ + DMOD_TEST_EXPECT_EQ(px(9, 25), 0xFFFFFF00u); + DMOD_TEST_EXPECT_EQ(libdmview_set_int(g_view, "tick", 20), 0); + DMOD_TEST_EXPECT_EQ(libdmview_render(g_view, &g_s32, NULL), 1); /* Seen: drawn */ touch(1, 15, 15, 50); touch(0, 0, 0, 60); DMOD_TEST_EXPECT_EQ(libdmview_get_int(g_view, "below", &value), 0); From a1d97b69b195660f636d6fbc6dac963b379e77dd Mon Sep 17 00:00:00 2001 From: Patryk Kubiak Date: Sun, 11 Oct 2026 23:43:11 +0200 Subject: [PATCH 2/2] dmview: with no view to show, leave what the display shows The service filled its display black whenever it had no view - also at its start, before any application claimed the display: the splash screen the display driver puts up at boot was wiped for a black screen until the application's view came (dmod-boot's Renode test of the splash screen failed once the board started dmview). Only a view that was shown is cleared away now. Co-Authored-By: Claude Opus 5.5 --- apps/dmview/src/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/dmview/src/main.c b/apps/dmview/src/main.c index c12c3d8..5cf0ee2 100644 --- a/apps/dmview/src/main.c +++ b/apps/dmview/src/main.c @@ -392,6 +392,7 @@ static void show_view(service_t* s, char* path) Dmod_Free(path); return; } + bool was_shown = s->view != NULL; libdmview_close(s->view); s->view = NULL; if (s->view_path != NULL) @@ -400,6 +401,8 @@ static void show_view(service_t* s, char* path) if (path == NULL) { DMOD_LOG_WARN("dmview: no view for %s (DMVIEW_VIEWS, DMVIEW_DEFAULT)\n", s->name); + if (!was_shown) + return; /* What the display shows - a splash screen - stays */ dmdrvi_gfx_fill_rect_t all = { 0, 0, s->info.width, s->info.height, 0xFF000000u }; (void)Dmod_Ioctl(s->gfx, DMDRVI_IOCTL_GFX_FILL_RECT, &all); present(s, NULL);