From c145d19e853cbb4c05bd03ae061b73118868c6ea Mon Sep 17 00:00:00 2001 From: Christian Soffke Date: Fri, 24 Apr 2026 19:25:52 +0200 Subject: [PATCH] gui: align display updates, reduce UI glitches Based on commits ce33902 and 8990d52 (without PictureFlow) from "Rockpod" fork by Nux Li (https://github.com/nuxcodes/rockpod), with some adjustments. Addresses flickering when: - plugin is opened/closed - activity changes - theme is toggled - QuickScreen is opened In these cases, skin_render will not immediately update the display anymore, but instead will wait until the UI viewport is ready to be drawn as well, so we don't produce unnecessary visual glitches. Change-Id: I8bed8f06221d3e767a32450f199e69d742bc61cd --- apps/gui/bitmap/list-skinned.c | 3 +-- apps/gui/bitmap/list.c | 3 +-- apps/gui/quickscreen.c | 3 +-- apps/gui/skin_engine/skin_engine.h | 5 +++++ apps/gui/skin_engine/skin_render.c | 28 +++++++++++++++++++++++++++- apps/gui/viewport.c | 4 ++++ apps/gui/wps.c | 4 ++++ apps/misc.c | 13 +++++++++++++ apps/plugin.c | 3 +++ 9 files changed, 59 insertions(+), 7 deletions(-) diff --git a/apps/gui/bitmap/list-skinned.c b/apps/gui/bitmap/list-skinned.c index 64a7f49ad6..b9bd18c2d1 100644 --- a/apps/gui/bitmap/list-skinned.c +++ b/apps/gui/bitmap/list-skinned.c @@ -281,8 +281,7 @@ bool skinlist_draw(struct screen *display, struct gui_synclist *list) } current_column = -1; current_row = -1; - display->set_viewport(parent); - display->update_viewport(); + skin_render_deferred(display, parent); current_drawing_line = list->selected_item; return true; } diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index 1d4942839a..34ae79e316 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c @@ -446,8 +446,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) callback_draw_item(&list_info); } - display->set_viewport(parent); - display->update_viewport(); + skin_render_deferred(display, parent); display->set_viewport(last_vp); } diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c index b76a4fd386..a5712ed66c 100644 --- a/apps/gui/quickscreen.c +++ b/apps/gui/quickscreen.c @@ -242,8 +242,7 @@ static void gui_quickscreen_draw(const struct gui_quickscreen *qs, (vp_icons->width/2) - 4, vp_icons->height - 8, 7, 8); } - display->set_viewport(parent); - display->update_viewport(); + skin_render_deferred(display, parent); display->set_viewport(last_vp); } diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h index fbaf1bf68a..f753dc8087 100644 --- a/apps/gui/skin_engine/skin_engine.h +++ b/apps/gui/skin_engine/skin_engine.h @@ -51,6 +51,11 @@ void skin_disarm_touchregions(struct gui_wps *gwps); void skin_update(enum skinnable_screens skin, enum screen_type screen, unsigned int update_type); +/* Defer updates in skin_render */ +void skin_defer_rendering(bool deferred); +/* Render viewport together with deferred updates */ +void skin_render_deferred(struct screen *display, struct viewport *vp); + bool skin_has_sbs(struct gui_wps *gwps); diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c index 77443eb8eb..8a7cea5414 100644 --- a/apps/gui/skin_engine/skin_render.c +++ b/apps/gui/skin_engine/skin_render.c @@ -84,6 +84,8 @@ static void skin_render_playlistviewer(struct playlistviewer* viewer, unsigned long refresh_type); static char* skin_buffer; +static bool defer_rendering; +static bool dirty; static inline struct skin_element* get_child(OFFSETTYPE(struct skin_element**) children, int child) @@ -841,6 +843,27 @@ void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps, wps_display_images(gwps, &skin_viewport->vp); } +void skin_defer_rendering(bool deferred) +{ + defer_rendering = deferred; +} + +void skin_render_deferred(struct screen *display, struct viewport *vp) +{ + if (dirty) + { + dirty = false; + display->set_viewport(NULL); + display->update(); + sb_skin_force_next_update(); + } + else + { + display->set_viewport(vp); + display->update_viewport(); + } +} + void skin_render(struct gui_wps *gwps, unsigned refresh_mode) { const int vp_is_appearing = (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE); @@ -940,7 +963,10 @@ void skin_render(struct gui_wps *gwps, unsigned refresh_mode) } /* Restore the default viewport */ display->set_viewport_ex(NULL, VP_FLAG_VP_SET_CLEAN); - display->update(); + if (defer_rendering) + dirty = true; + else + display->update(); } static __attribute__((noinline)) diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c index b50cba7d74..6bc51401f5 100644 --- a/apps/gui/viewport.c +++ b/apps/gui/viewport.c @@ -154,7 +154,11 @@ static void toggle_theme(enum screen_type screen, bool force) } intptr_t force = first_boot?0:1; + skin_defer_rendering(true); send_event(GUI_EVENT_ACTIONUPDATE, (void*)force); + skin_defer_rendering(false); + if (!first_boot) + sb_skin_force_next_update(); } else { diff --git a/apps/gui/wps.c b/apps/gui/wps.c index 0e52323dc2..6d8597d495 100644 --- a/apps/gui/wps.c +++ b/apps/gui/wps.c @@ -533,7 +533,11 @@ static void gwps_leave_wps(bool theme_enabled) viewports drawn by the WPS. May need further thought... */ struct wps_data *sbs = skin_get_gwps(CUSTOM_STATUSBAR, i)->data; if (gwps->data->use_extra_framebuffer && sbs->use_extra_framebuffer) + { + skin_defer_rendering(true); skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + } #endif viewportmanager_theme_undo(i, skin_has_sbs(gwps)); } diff --git a/apps/misc.c b/apps/misc.c index 1780ccd9fe..0c5d73d2ee 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -65,6 +65,7 @@ #include "list.h" #include "fixedpoint.h" #include "open_plugin.h" +#include "statusbar-skinned.h" #include "debug.h" @@ -1795,8 +1796,14 @@ static void push_current_activity_refresh(enum current_activity screen, bool ref { skinlist_set_cfg(i, NULL); if (refresh) + { + skin_defer_rendering(true); skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + } } + if (refresh) + sb_skin_force_next_update(); } static void pop_current_activity_refresh(bool refresh) @@ -1806,8 +1813,14 @@ static void pop_current_activity_refresh(bool refresh) { skinlist_set_cfg(i, NULL); if (refresh) + { + skin_defer_rendering(true); skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + } } + if (refresh) + sb_skin_force_next_update(); } void push_current_activity(enum current_activity screen) diff --git a/apps/plugin.c b/apps/plugin.c index 57f0cb3d97..6c689b21e6 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -1022,8 +1022,11 @@ int plugin_load(const char* plugin, const void* parameter) pop_current_activity_without_refresh(); if (get_current_activity() != ACTIVITY_WPS) { + skin_defer_rendering(true); FOR_NB_SCREENS(i) skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + sb_skin_force_next_update(); } if (!pfn_tsr_exit)