feat(core/eckhart): haptic feedback for HTC animation
What changed, and why it matters
This commit adds vibration feedback to the 'hold to confirm' animation on a specific Trezor hardware wallet layout (Eckhart). It only introduces haptic effects behind a compile-time feature flag and does not change security logic, transaction confirmation behavior, or access controls.
No security action required; treat as a normal feature review for haptic behavior and power/usability impact.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds optional haptic feedback to the HoldToConfirmAnim component and the ActionBar confirmation path in the Eckhart UI layout. It imports the haptic HAL and, when the ‘haptic’ feature is enabled, plays a custom haptic effect scaled to the animation progress and a ‘HoldToConfirm’ effect when the long-press is confirmed. The changes are gated by #[cfg(feature = ‘haptic’)] and do not alter confirmation state machines or security checks.
Changed components
core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rscore/embed/rust/src/ui/layout_eckhart/firmware/hold_to_confirm.rsInspect captured patch +40 / −3
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs
index c5cdb43c..5ea07de6 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs
@@ -10,6 +10,9 @@ use crate::{
},
};
+#[cfg(feature = "haptic")]
+use crate::trezorhal::haptic::{self, HapticEffect};
+
use super::{
super::component::{Button, ButtonMsg},
theme, HoldToConfirmAnim,
@@ -267,6 +270,8 @@ impl ActionBar {
ctx.enable_swipe();
} else {
// Animations disabled, return confirmed
+ #[cfg(feature = "haptic")]
+ haptic::play(HapticEffect::HoldToConfirm);
return Some(ActionBarMsg::Confirmed);
}
}
@@ -278,7 +283,12 @@ impl ActionBar {
ctx.enable_swipe();
}
}
- (ButtonMsg::Clicked, false) | (ButtonMsg::LongPressed, true) => {
+ (ButtonMsg::LongPressed, true) => {
+ #[cfg(feature = "haptic")]
+ haptic::play(HapticEffect::HoldToConfirm);
+ return Some(ActionBarMsg::Confirmed);
+ }
+ (ButtonMsg::Clicked, false) => {
return Some(ActionBarMsg::Confirmed);
}
_ => {}
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/hold_to_confirm.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/hold_to_confirm.rs
index ab3ba1d1..20b2d840 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/hold_to_confirm.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/hold_to_confirm.rs
@@ -15,6 +15,12 @@ use super::{
constant::SCREEN,
};
+#[cfg(feature = "haptic")]
+use pareen;
+
+#[cfg(feature = "haptic")]
+use crate::trezorhal::haptic;
+
/// A component that displays a border that grows from the bottom of the screen
/// to the top. The animation is parametrizable by color and duration.
pub struct HoldToConfirmAnim {
@@ -145,13 +151,17 @@ impl Component for HoldToConfirmAnim {
// Growing animation
if self.is_active() {
+ let elapsed = self.timer.elapsed();
// override header with custom text
- if self.timer.elapsed() > Self::HEADER_OVERLAY_DELAY {
+ if elapsed > Self::HEADER_OVERLAY_DELAY {
self.render_header_overlay(target);
}
// growing border
- let (clip, top_gap) = self.get_clips(self.timer.elapsed());
+ let (clip, top_gap) = self.get_clips(elapsed);
self.render_clipped_border(clip, top_gap, u8::MAX, target);
+
+ #[cfg(feature = "haptic")]
+ haptic::play_custom(self.get_haptic(elapsed), 100);
}
}
}
@@ -290,6 +300,23 @@ impl HoldToConfirmAnim {
_ => (SCREEN, TOP_GAP_ZERO),
}
}
+
+ #[cfg(feature = "haptic")]
+ fn get_haptic(&self, elapsed: Duration) -> i8 {
+ // Normalize elapsed time
+ let progress = (elapsed / self.total_duration).clamp(0.0, 1.0);
+
+ // Create a linear easing from 0.0 to 1.0 over normalized progress
+ let ease = pareen::constant(0.0).seq_ease_in(
+ 0.0,
+ easer::functions::Linear,
+ 1.0,
+ pareen::constant(1.0),
+ );
+
+ // Scale eased value to 0–100
+ (100.0 * ease.eval(progress)) as i8
+ }
}
#[cfg(test)]
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.