mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
fix(core): ensure drop is called on layout objects
[no changelog]
This commit is contained in:
parent
813d9b7687
commit
e03d404dca
@ -22,6 +22,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_NORMAL;
|
MP_QSTR_NORMAL;
|
||||||
MP_QSTR_TR;
|
MP_QSTR_TR;
|
||||||
MP_QSTR_TranslationsHeader;
|
MP_QSTR_TranslationsHeader;
|
||||||
|
MP_QSTR___del__;
|
||||||
MP_QSTR___dict__;
|
MP_QSTR___dict__;
|
||||||
MP_QSTR___name__;
|
MP_QSTR___name__;
|
||||||
MP_QSTR_account;
|
MP_QSTR_account;
|
||||||
|
@ -208,29 +208,45 @@ where
|
|||||||
/// Same as `Child` but also handles screen clearing when layout is first
|
/// Same as `Child` but also handles screen clearing when layout is first
|
||||||
/// painted.
|
/// painted.
|
||||||
pub struct Root<T> {
|
pub struct Root<T> {
|
||||||
inner: Child<T>,
|
inner: Option<Child<T>>,
|
||||||
marked_for_clear: bool,
|
marked_for_clear: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Root<T> {
|
impl<T> Root<T> {
|
||||||
pub fn new(component: T) -> Self {
|
pub fn new(component: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Child::new(component),
|
inner: Some(Child::new(component)),
|
||||||
marked_for_clear: true,
|
marked_for_clear: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inner_mut(&mut self) -> &mut Child<T> {
|
||||||
|
if let Some(ref mut c) = self.inner {
|
||||||
|
c
|
||||||
|
} else {
|
||||||
|
fatal_error!("deallocated", "Root object is deallocated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inner(&self) -> &Child<T> {
|
pub fn inner(&self) -> &Child<T> {
|
||||||
&self.inner
|
if let Some(ref c) = self.inner {
|
||||||
|
c
|
||||||
|
} else {
|
||||||
|
fatal_error!("deallocated", "Root object is deallocated")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_paint(&mut self) {
|
pub fn skip_paint(&mut self) {
|
||||||
self.inner.skip_paint()
|
self.inner_mut().skip_paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_screen(&mut self) {
|
pub fn clear_screen(&mut self) {
|
||||||
self.marked_for_clear = true;
|
self.marked_for_clear = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self) {
|
||||||
|
self.inner = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for Root<T>
|
impl<T> Component for Root<T>
|
||||||
@ -240,15 +256,15 @@ where
|
|||||||
type Msg = T::Msg;
|
type Msg = T::Msg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
self.inner.place(bounds)
|
self.inner_mut().place(bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
let msg = self.inner.event(ctx, event);
|
let msg = self.inner_mut().event(ctx, event);
|
||||||
if ctx.needs_repaint_root() {
|
if ctx.needs_repaint_root() {
|
||||||
self.marked_for_clear = true;
|
self.marked_for_clear = true;
|
||||||
let mut dummy_ctx = EventCtx::new();
|
let mut dummy_ctx = EventCtx::new();
|
||||||
let paint_msg = self.inner.event(&mut dummy_ctx, Event::RequestPaint);
|
let paint_msg = self.inner_mut().event(&mut dummy_ctx, Event::RequestPaint);
|
||||||
assert!(paint_msg.is_none());
|
assert!(paint_msg.is_none());
|
||||||
assert!(dummy_ctx.timers.is_empty());
|
assert!(dummy_ctx.timers.is_empty());
|
||||||
}
|
}
|
||||||
@ -256,7 +272,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
if self.marked_for_clear && self.inner.will_paint() {
|
if self.marked_for_clear && self.inner().will_paint() {
|
||||||
self.marked_for_clear = false;
|
self.marked_for_clear = false;
|
||||||
display::clear()
|
display::clear()
|
||||||
}
|
}
|
||||||
@ -264,12 +280,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render<'s>(&self, target: &mut impl Renderer<'s>) {
|
fn render<'s>(&self, target: &mut impl Renderer<'s>) {
|
||||||
self.inner.render(target);
|
self.inner().render(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_bounds")]
|
#[cfg(feature = "ui_bounds")]
|
||||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||||
self.inner.bounds(sink)
|
self.inner().bounds(sink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +295,7 @@ where
|
|||||||
T: crate::trace::Trace,
|
T: crate::trace::Trace,
|
||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
self.inner.trace(t)
|
self.inner().trace(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ pub trait ObjComponent: MaybeTrace {
|
|||||||
fn obj_bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
|
fn obj_bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
|
||||||
fn obj_skip_paint(&mut self) {}
|
fn obj_skip_paint(&mut self) {}
|
||||||
fn obj_request_clear(&mut self) {}
|
fn obj_request_clear(&mut self) {}
|
||||||
|
fn obj_delete(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ObjComponent for Root<T>
|
impl<T> ObjComponent for Root<T>
|
||||||
@ -105,6 +106,10 @@ where
|
|||||||
fn obj_request_clear(&mut self) {
|
fn obj_request_clear(&mut self) {
|
||||||
self.clear_screen()
|
self.clear_screen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn obj_delete(&mut self) {
|
||||||
|
self.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `LayoutObj` is a GC-allocated object exported to MicroPython, with type
|
/// `LayoutObj` is a GC-allocated object exported to MicroPython, with type
|
||||||
@ -133,15 +138,25 @@ impl LayoutObj {
|
|||||||
let root =
|
let root =
|
||||||
unsafe { Gc::from_raw(Gc::into_raw(Gc::new(wrapped_root)?) as *mut dyn ObjComponent) };
|
unsafe { Gc::from_raw(Gc::into_raw(Gc::new(wrapped_root)?) as *mut dyn ObjComponent) };
|
||||||
|
|
||||||
Gc::new(Self {
|
// SAFETY: This is a Python object and hase a base as first element
|
||||||
base: Self::obj_type().as_base(),
|
unsafe {
|
||||||
inner: RefCell::new(LayoutObjInner {
|
Gc::new_with_custom_finaliser(Self {
|
||||||
root,
|
base: Self::obj_type().as_base(),
|
||||||
event_ctx: EventCtx::new(),
|
inner: RefCell::new(LayoutObjInner {
|
||||||
timer_fn: Obj::const_none(),
|
root,
|
||||||
page_count: 1,
|
event_ctx: EventCtx::new(),
|
||||||
}),
|
timer_fn: Obj::const_none(),
|
||||||
})
|
page_count: 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn obj_delete(&self) {
|
||||||
|
let mut inner = self.inner.borrow_mut();
|
||||||
|
|
||||||
|
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
|
||||||
|
unsafe { Gc::as_mut(&mut inner.root) }.obj_delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_first_paint(&self) {
|
pub fn skip_first_paint(&self) {
|
||||||
@ -291,6 +306,7 @@ impl LayoutObj {
|
|||||||
Qstr::MP_QSTR_request_complete_repaint => obj_fn_1!(ui_layout_request_complete_repaint).as_obj(),
|
Qstr::MP_QSTR_request_complete_repaint => obj_fn_1!(ui_layout_request_complete_repaint).as_obj(),
|
||||||
Qstr::MP_QSTR_trace => obj_fn_2!(ui_layout_trace).as_obj(),
|
Qstr::MP_QSTR_trace => obj_fn_2!(ui_layout_trace).as_obj(),
|
||||||
Qstr::MP_QSTR_bounds => obj_fn_1!(ui_layout_bounds).as_obj(),
|
Qstr::MP_QSTR_bounds => obj_fn_1!(ui_layout_bounds).as_obj(),
|
||||||
|
Qstr::MP_QSTR___del__ => obj_fn_1!(ui_layout_delete).as_obj(),
|
||||||
Qstr::MP_QSTR_page_count => obj_fn_1!(ui_layout_page_count).as_obj(),
|
Qstr::MP_QSTR_page_count => obj_fn_1!(ui_layout_page_count).as_obj(),
|
||||||
Qstr::MP_QSTR_button_request => obj_fn_1!(ui_layout_button_request).as_obj(),
|
Qstr::MP_QSTR_button_request => obj_fn_1!(ui_layout_button_request).as_obj(),
|
||||||
}),
|
}),
|
||||||
@ -526,3 +542,12 @@ extern "C" fn ui_layout_bounds(this: Obj) -> Obj {
|
|||||||
extern "C" fn ui_layout_bounds(_this: Obj) -> Obj {
|
extern "C" fn ui_layout_bounds(_this: Obj) -> Obj {
|
||||||
Obj::const_none()
|
Obj::const_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn ui_layout_delete(this: Obj) -> Obj {
|
||||||
|
let block = || {
|
||||||
|
let this: Gc<LayoutObj> = this.try_into()?;
|
||||||
|
this.obj_delete();
|
||||||
|
Ok(Obj::const_none())
|
||||||
|
};
|
||||||
|
unsafe { util::try_or_raise(block) }
|
||||||
|
}
|
||||||
|
@ -14,7 +14,6 @@ use crate::{
|
|||||||
shape::{self, Renderer},
|
shape::{self, Renderer},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use core::mem;
|
|
||||||
|
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::Label,
|
component::Label,
|
||||||
@ -344,10 +343,6 @@ impl Component for Lockscreen<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Event::Touch(TouchEvent::TouchEnd(_)) = event {
|
if let Event::Touch(TouchEvent::TouchEnd(_)) = event {
|
||||||
let bg_img = mem::replace(&mut self.bg_image, None);
|
|
||||||
if let Some(bg_img) = bg_img {
|
|
||||||
drop(bg_img);
|
|
||||||
}
|
|
||||||
return Some(HomescreenMsg::Dismissed);
|
return Some(HomescreenMsg::Dismissed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1532,6 +1532,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// def page_count(self) -> int:
|
/// def page_count(self) -> int:
|
||||||
/// """Return the number of pages in the layout object."""
|
/// """Return the number of pages in the layout object."""
|
||||||
///
|
///
|
||||||
|
/// def __del__(self) -> None:
|
||||||
|
/// """Calls drop on contents of the root component."""
|
||||||
|
///
|
||||||
/// class UiResult:
|
/// class UiResult:
|
||||||
/// """Result of a UI operation."""
|
/// """Result of a UI operation."""
|
||||||
/// pass
|
/// pass
|
||||||
|
@ -1679,6 +1679,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// def button_request(self) -> tuple[int, str] | None:
|
/// def button_request(self) -> tuple[int, str] | None:
|
||||||
/// """Return (code, type) of button request made during the last event or timer pass."""
|
/// """Return (code, type) of button request made during the last event or timer pass."""
|
||||||
///
|
///
|
||||||
|
/// def __del__(self) -> None:
|
||||||
|
/// """Calls drop on contents of the root component."""
|
||||||
|
///
|
||||||
/// class UiResult:
|
/// class UiResult:
|
||||||
/// """Result of a UI operation."""
|
/// """Result of a UI operation."""
|
||||||
/// pass
|
/// pass
|
||||||
|
@ -49,6 +49,8 @@ class LayoutObj(Generic[T]):
|
|||||||
"""Paint bounds of individual components on screen."""
|
"""Paint bounds of individual components on screen."""
|
||||||
def page_count(self) -> int:
|
def page_count(self) -> int:
|
||||||
"""Return the number of pages in the layout object."""
|
"""Return the number of pages in the layout object."""
|
||||||
|
def __del__(self) -> None:
|
||||||
|
"""Calls drop on contents of the root component."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
@ -1122,6 +1124,8 @@ class LayoutObj(Generic[T]):
|
|||||||
"""Return the number of pages in the layout object."""
|
"""Return the number of pages in the layout object."""
|
||||||
def button_request(self) -> tuple[int, str] | None:
|
def button_request(self) -> tuple[int, str] | None:
|
||||||
"""Return (code, type) of button request made during the last event or timer pass."""
|
"""Return (code, type) of button request made during the last event or timer pass."""
|
||||||
|
def __del__(self) -> None:
|
||||||
|
"""Calls drop on contents of the root component."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
# rust/src/ui/model_tt/layout.rs
|
||||||
|
@ -12,7 +12,11 @@ from apps.common.authorization import is_set_any_session
|
|||||||
|
|
||||||
|
|
||||||
async def busyscreen() -> None:
|
async def busyscreen() -> None:
|
||||||
await Busyscreen(busy_expiry_ms())
|
obj = Busyscreen(busy_expiry_ms())
|
||||||
|
try:
|
||||||
|
await obj
|
||||||
|
finally:
|
||||||
|
obj.__del__()
|
||||||
|
|
||||||
|
|
||||||
async def homescreen() -> None:
|
async def homescreen() -> None:
|
||||||
@ -42,12 +46,17 @@ async def homescreen() -> None:
|
|||||||
elif storage.device.get_experimental_features():
|
elif storage.device.get_experimental_features():
|
||||||
notification = TR.homescreen__title_experimental_mode
|
notification = TR.homescreen__title_experimental_mode
|
||||||
|
|
||||||
await Homescreen(
|
obj = Homescreen(
|
||||||
label=label,
|
label=label,
|
||||||
notification=notification,
|
notification=notification,
|
||||||
notification_is_error=notification_is_error,
|
notification_is_error=notification_is_error,
|
||||||
hold_to_lock=config.has_pin(),
|
hold_to_lock=config.has_pin(),
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
await obj
|
||||||
|
finally:
|
||||||
|
obj.__del__()
|
||||||
|
|
||||||
lock_device()
|
lock_device()
|
||||||
|
|
||||||
|
|
||||||
@ -58,10 +67,14 @@ async def _lockscreen(screensaver: bool = False) -> None:
|
|||||||
# Only show the lockscreen UI if the device can in fact be locked, or if it is
|
# Only show the lockscreen UI if the device can in fact be locked, or if it is
|
||||||
# and OLED device (in which case the lockscreen is a screensaver).
|
# and OLED device (in which case the lockscreen is a screensaver).
|
||||||
if can_lock_device() or screensaver:
|
if can_lock_device() or screensaver:
|
||||||
await Lockscreen(
|
obj = Lockscreen(
|
||||||
label=storage.device.get_label(),
|
label=storage.device.get_label(),
|
||||||
coinjoin_authorized=is_set_any_session(MessageType.AuthorizeCoinJoin),
|
coinjoin_authorized=is_set_any_session(MessageType.AuthorizeCoinJoin),
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
await obj
|
||||||
|
finally:
|
||||||
|
obj.__del__()
|
||||||
# Otherwise proceed directly to unlock() call. If the device is already unlocked,
|
# Otherwise proceed directly to unlock() call. If the device is already unlocked,
|
||||||
# it should be a no-op storage-wise, but it resets the internal configuration
|
# it should be a no-op storage-wise, but it resets the internal configuration
|
||||||
# to an unlocked state.
|
# to an unlocked state.
|
||||||
|
@ -62,6 +62,7 @@ async def bootscreen() -> None:
|
|||||||
label=storage.device.get_label(), bootscreen=True
|
label=storage.device.get_label(), bootscreen=True
|
||||||
)
|
)
|
||||||
await lockscreen
|
await lockscreen
|
||||||
|
lockscreen.__del__()
|
||||||
await verify_user_pin()
|
await verify_user_pin()
|
||||||
storage.init_unlocked()
|
storage.init_unlocked()
|
||||||
allow_all_loader_messages()
|
allow_all_loader_messages()
|
||||||
|
@ -40,6 +40,9 @@ class RustLayout(ui.Layout):
|
|||||||
self._send_button_request()
|
self._send_button_request()
|
||||||
self.backlight_level = ui.BacklightLevels.NORMAL
|
self.backlight_level = ui.BacklightLevels.NORMAL
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.layout.__del__()
|
||||||
|
|
||||||
def set_timer(self, token: int, deadline: int) -> None:
|
def set_timer(self, token: int, deadline: int) -> None:
|
||||||
self.timer.schedule(deadline, token)
|
self.timer.schedule(deadline, token)
|
||||||
|
|
||||||
|
@ -44,6 +44,9 @@ class RustLayout(LayoutParentType[T]):
|
|||||||
self.layout.attach_timer_fn(self.set_timer)
|
self.layout.attach_timer_fn(self.set_timer)
|
||||||
self._send_button_request()
|
self._send_button_request()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.layout.__del__()
|
||||||
|
|
||||||
def set_timer(self, token: int, deadline: int) -> None:
|
def set_timer(self, token: int, deadline: int) -> None:
|
||||||
self.timer.schedule(deadline, token)
|
self.timer.schedule(deadline, token)
|
||||||
|
|
||||||
|
@ -46,6 +46,9 @@ class RustLayout(LayoutParentType[T]):
|
|||||||
self._send_button_request()
|
self._send_button_request()
|
||||||
self.backlight_level = ui.BacklightLevels.NORMAL
|
self.backlight_level = ui.BacklightLevels.NORMAL
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.layout.__del__()
|
||||||
|
|
||||||
def set_timer(self, token: int, deadline: int) -> None:
|
def set_timer(self, token: int, deadline: int) -> None:
|
||||||
self.timer.schedule(deadline, token)
|
self.timer.schedule(deadline, token)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user