Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions crates/shell/src/module_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,63 @@ mod assistant_tests {
assert!(host.teardown(&mut cx, 1));
}

/// A Daycast reminder opens Rinx's native sheet without sending anything.
#[cfg(feature = "app-rinx")]
#[test]
fn rinx_reminder_confirmation_does_not_stop_the_module() {
if super::run_with_isolated_module_data("module_host::assistant_tests::rinx_reminder_confirmation_does_not_stop_the_module") { return; }
let _one_rinx = super::RINX_INSTANCE_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let mut cx = Cx::new(Box::new(|_, _| {}));
cx.with_vm(makepad_widgets::script_mod);
let mut host = crate::module_host::ModuleHost::default();
host.apply_style(&mut cx, &crate::desktop_style::StyleSheet::load(crate::desktop_style::DesktopStyle::Macos));
let module = &rinx::module::RINX_MODULE;
host.create(&mut cx, 7, module, module.open_schema().empty_open().unwrap(), dvec2(400.0, 700.0)).unwrap();
let tile = cx.with_vm(|vm| {
script_eval!(vm, { mod.wm_theme = { background: #1a1b26 } });
crate::module_view::script_mod(vm);
let value = script_eval!(vm, { use mod.widgets.* MpModuleView {} });
WidgetRef::script_from_value(vm, value)
});
let instance = host.get(7).unwrap();
tile.borrow_mut::<crate::module_view::MpModuleView>().unwrap().set_root(&mut cx, 7, instance.vm_id, instance.root.clone());
let draw_frame = |cx: &mut Cx| {
let pass = DrawPass::new(cx);
pass.set_size(cx, dvec2(800.0, 600.0));
let mut list = DrawList2d::new(cx);
let overlay = cx.with_vm(Overlay::script_new);
let event = DrawEvent::default();
let mut draw = CxDraw::new(cx, &event);
let mut cx = Cx2d::new(&mut draw);
cx.begin_pass(&pass, Some(1.0));
list.begin_always(&mut cx);
overlay.begin(&mut cx);
cx.begin_root_turtle(dvec2(800.0, 600.0), Layout::flow_right());
tile.draw_walk_all(&mut cx, &mut Scope::empty(), Walk::fixed(400.0, 600.0));
cx.end_turtle();
overlay.end(&mut cx);
list.end(&mut cx);
cx.end_pass(&pass);
};
draw_frame(&mut cx);
assert!(host.take_faults(&mut cx).is_empty(), "Rinx initial draw: {:?}", host.get(7).unwrap().failure());
let prompt = rinx::assistant::Prompt {
call_id: "daycast-rinx-test".into(), account: "@test:example.org".into(),
room: rinx::assistant::RoomRef { id: "!test".into(), name: "家人聊天室".into() },
kind: rinx::assistant::PromptKind::Send { text: "Daycast 家庭天气提醒 · 北京\n天气变化,注意适时添减衣物。".into() },
deadline: std::time::Instant::now() + std::time::Duration::from_secs(120),
};
for action in [rinx::assistant::AssistantAction::ShowPrompt(prompt.clone()), rinx::assistant::AssistantAction::HidePrompt, rinx::assistant::AssistantAction::ShowPrompt(prompt)] {
host.dispatch(&mut cx, 7, "Daycast reminder confirmation", |cx, root| {
root.handle_event(cx, &makepad_widgets::Event::Actions(vec![Box::new(action)]), &mut makepad_widgets::Scope::empty());
});
draw_frame(&mut cx);
assert!(host.take_faults(&mut cx).is_empty(), "Rinx stopped: {:?}", host.get(7).unwrap().failure());
}
assert!(host.teardown(&mut cx, 7));
}


/// The real Rinx module: hosted from creation, with the shell's service,
/// and no kernel started by creating it (ADR 0007 criterion 7).
#[cfg(feature = "app-rinx")]
Expand Down
9 changes: 7 additions & 2 deletions runtime-patches.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"base_revision": "d653bc4493619ab979ccff165cf48c0c63b77d19",
"patch": "tools/runtime-patches/makepad-settings.patch",
"sha256": "3a79cddddc89dc4d064a9f0bdd17dc8111dcab9c796a334d95f61ecb9d5011aa",
"tree": "d047e32fdfb91aa26b95a62464645020bfa6a4f7",
"tree": "0e0589c15e7d6aa8dd3f5b73a612c5137400b257",
"product_fixes": [
"Settings integration: Android IME synchronization, accessibility and reduced motion, GLES program-cache validation, obscured-gesture cancellation and scroll-position observation.",
"Contained-app isolation and secret-input restrictions come from the pinned upstream runtime; the Settings overlay preserves those gates.",
Expand Down Expand Up @@ -49,7 +49,8 @@
"An OS resume restarts only the Video playback its pause stopped, including a start it cancelled; a player paused by the person, or paused or stopped by its app during the pause, stays paused (#406).",
"Camera capture and recording use explicit per-call boolean audio/library options, defaulting to silent app-local output. Permission availability never opts into recording audio or exporting captures; async microphone checks retain capture intent and background provenance. Unsupported library export fails before capture.",
"A hidden Android native composer remembers animated IME insets without requesting a root layout every frame; showing it applies the latest inset before its first visible frame.",
"Launch-input hardening: the Studio connection and the OCTOS_PROXY app HTTP proxy, which Makepad itself acts on from an Android Intent extra, are honoured only when the build opts into launch-time development input (debuggable, or the manifest meta-data dev.makepad.LAUNCH_STUDIO). A shipped build ignores them and drops any Studio host a development build saved. The exported activity would otherwise let another installed app point it at a host or a proxy."
"Launch-input hardening: the Studio connection and the OCTOS_PROXY app HTTP proxy, which Makepad itself acts on from an Android Intent extra, are honoured only when the build opts into launch-time development input (debuggable, or the manifest meta-data dev.makepad.LAUNCH_STUDIO). A shipped build ignores them and drops any Studio host a development build saved. The exported activity would otherwise let another installed app point it at a host or a proxy.",
"Hosted module UI roots stay scoped to their Splash isolate instead of replacing the process-global widget tree, preventing Rinx from stopping during reminder-prompt drawing."
],
"stacked": [
{
Expand Down Expand Up @@ -195,6 +196,10 @@
{
"patch": "tools/runtime-patches/makepad-launch-input-hardening.patch",
"sha256": "8eaafef3ef0591bec2a25c05d5187564d5dfb87a32e6b937b6d9c4e9f6fa5b84"
},
{
"patch": "tools/runtime-patches/makepad-isolate-ui-root.patch",
"sha256": "8683242f22f621b33f5683b9ec47ce76560b59dbc7f5972d4dd1a2f4eb9db915"
}
]
}
Expand Down
39 changes: 39 additions & 0 deletions tools/runtime-patches/makepad-isolate-ui-root.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
diff --git a/widgets/src/widget_tree.rs b/widgets/src/widget_tree.rs
index f1dded6..7adb2f5 100644
--- a/widgets/src/widget_tree.rs
+++ b/widgets/src/widget_tree.rs
@@ -8,7 +8,9 @@
crate::radio_button::RadioButton,
crate::text_input::TextInput,
crate::widget::{WidgetRef, WidgetRegistry, WidgetUid, WidgetWeakRef},
- crate::widget_async::update_global_ui_handle,
+ crate::widget_async::{
+ current_splash_vm_id, inject_splash_ui_handle, update_global_ui_handle, MAIN_SPLASH_VM_ID,
+ },
crate::window::Window,
std::any::TypeId,
std::cell::RefCell,
@@ -3179,13 +3181,21 @@ pub struct FlatTreeRow {
}

pub fn set_ui_root(cx: &mut Cx, ui: &WidgetRef) {
+ let root_uid = ui.widget_uid();
+ let vm_id = current_splash_vm_id(cx);
+ if vm_id != MAIN_SPLASH_VM_ID {
+ // A hosted module's Root belongs to its isolate. Keep it there instead
+ // of replacing the shell's process-wide widget tree with a VM-owned
+ // WidgetRef that becomes invalid when that module closes.
+ inject_splash_ui_handle(cx, vm_id, root_uid);
+ return;
+ }
let state = get_or_init_state(cx);
state.tree.set_root_widget(ui.clone());
cx.widget_tree_dump_callback = Some(compact_widget_tree_dump_callback);
cx.widget_query_callback = Some(widget_query_callback);
cx.widget_snapshot_callback = Some(widget_snapshot_callback);
cx.tweak_callback = Some(crate::tweaker::tweak_callback);
- let root_uid = ui.widget_uid();
update_global_ui_handle(cx, root_uid);
}