Skip to content

Commit 04099df

Browse files
committed
MENU/XMB: fix stacking tweens on categories_x_pos during rapid horizontal input
gfx_animation_push appends rather than replaces, and the two horizontal tab-scroll animation sites (xmb_list_switch and the drag-release handler in xmb_pointer_up) were both pushing with tag = -1 — the animation system's "no tag" sentinel, which gfx_animation_kill_by_tag explicitly refuses to act on. The consequence is that rapid left/right input, or a drag-release landing on an in-flight tab-switch tween, stacks two or more tweens on xmb->categories_x_pos. Each frame gfx_animation_update iterates the tween list and every matching tween writes the subject in order, so last-write-wins by append order. The visible settled position is non-deterministic during overlap, and the float never reliably matches its intended target until all stacked tweens have finished on their own timelines. Switch both sites to a unique tag, (uintptr_t)&xmb->categories_x_pos — the same pattern xmb_context_reset_horizontal_list already uses for xmb->x — and kill any in-flight tween on that tag before pushing the new one. Also kill on the non-animated branches: direct assignments would otherwise be clobbered on the next tick if a tween were still running (e.g. menu_horizontal_animation toggled off mid-animation, or xmb_context_reset_horizontal_list invoked during a transition). xmb_init is not touched because the handle is being allocated there and no tweens can exist. Incidentally resolves the TODO/FIXME comment about the sign-conversion warning on "anim_entry.tag = -1". No user-visible behaviour change during normal single-step navigation, where only one tween is ever active. Fast input and drag-interrupt cases now resolve cleanly to the last-pressed target.
1 parent d4e75bb commit 04099df

1 file changed

Lines changed: 39 additions & 3 deletions

File tree

menu/drivers/xmb.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2805,19 +2805,39 @@ static void xmb_list_switch(xmb_handle_t *xmb)
28052805
/* Horizontal tab icon scroll */
28062806
if (horizontal_animation)
28072807
{
2808+
uintptr_t cat_tag = (uintptr_t)&xmb->categories_x_pos;
2809+
28082810
anim_entry.duration = XMB_DELAY;
28092811
anim_entry.target_value = xmb->icon_spacing_horizontal * -(float)xmb->categories_selection_ptr;
28102812
anim_entry.subject = &xmb->categories_x_pos;
28112813
anim_entry.easing_enum = XMB_EASING_XY;
2812-
/* TODO/FIXME - integer conversion resulted in change of sign */
2813-
anim_entry.tag = -1;
2814+
anim_entry.tag = cat_tag;
28142815
anim_entry.cb = NULL;
28152816

2817+
/* Kill any in-flight tween on categories_x_pos before pushing a
2818+
* new one. gfx_animation_push appends rather than replaces, so
2819+
* rapid left/right input would otherwise stack two or more tweens
2820+
* that fight each frame over the same float (last-write-wins in
2821+
* iteration order) and the visible settled position would depend
2822+
* on which one happens to be appended last. Tagging the tween
2823+
* and killing prior ones makes back-to-back transitions atomic
2824+
* and gives a reliable "settled" predicate for callers that want
2825+
* to know when the horizontal scroll is done. */
2826+
gfx_animation_kill_by_tag(&cat_tag);
2827+
28162828
if (anim_entry.subject)
28172829
gfx_animation_push(&anim_entry);
28182830
}
28192831
else
2832+
{
2833+
/* Direct-set path: also kill any in-flight tween so it doesn't
2834+
* clobber the assignment on the next animation tick. This matters
2835+
* when menu_horizontal_animation was toggled off while a previous
2836+
* tween was still running. */
2837+
uintptr_t cat_tag = (uintptr_t)&xmb->categories_x_pos;
2838+
gfx_animation_kill_by_tag(&cat_tag);
28202839
xmb->categories_x_pos = xmb->icon_spacing_horizontal * -(float)xmb->categories_selection_ptr;
2840+
}
28212841

28222842
/* Check if we are to have horizontal animations. */
28232843
if (horizontal_animation)
@@ -3029,8 +3049,13 @@ static void xmb_context_reset_horizontal_list(xmb_handle_t *xmb)
30293049
int depth = 1;
30303050
size_t list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL);
30313051
uintptr_t tag = (uintptr_t)&xmb->x;
3052+
uintptr_t cat_tag = (uintptr_t)&xmb->categories_x_pos;
30323053

30333054
gfx_animation_kill_by_tag(&tag);
3055+
/* Also kill tweens on categories_x_pos: a pending tween would
3056+
* otherwise clobber the direct assignment below on the next
3057+
* animation tick. */
3058+
gfx_animation_kill_by_tag(&cat_tag);
30343059

30353060
xmb->categories_x_pos = xmb->icon_spacing_horizontal * -(float)xmb->categories_selection_ptr;
30363061

@@ -10040,18 +10065,29 @@ static int xmb_pointer_up(void *userdata,
1004010065
if (horizontal_animation)
1004110066
{
1004210067
gfx_animation_ctx_entry_t anim_entry;
10068+
uintptr_t cat_tag = (uintptr_t)&xmb->categories_x_pos;
10069+
1004310070
anim_entry.duration = XMB_DELAY;
1004410071
anim_entry.target_value = target_x;
1004510072
anim_entry.subject = &xmb->categories_x_pos;
1004610073
anim_entry.easing_enum = EASING_OUT_QUAD;
10047-
anim_entry.tag = -1;
10074+
anim_entry.tag = cat_tag;
1004810075
anim_entry.cb = NULL;
1004910076

10077+
/* See xmb_list_switch for the rationale — same unique-tag
10078+
* kill-before-push pattern so drag-release doesn't stack
10079+
* a tween on top of an in-flight tab-switch tween. */
10080+
gfx_animation_kill_by_tag(&cat_tag);
10081+
1005010082
if (anim_entry.subject)
1005110083
gfx_animation_push(&anim_entry);
1005210084
}
1005310085
else
10086+
{
10087+
uintptr_t cat_tag = (uintptr_t)&xmb->categories_x_pos;
10088+
gfx_animation_kill_by_tag(&cat_tag);
1005410089
xmb->categories_x_pos = target_x;
10090+
}
1005510091
}
1005610092

1005710093
xmb->drag_mode = XMB_DRAG_NONE;

0 commit comments

Comments
 (0)