1use super::composer::Observer;
14use super::{with_composer, with_composer_if_idle};
15use crate::schema::{DesignSystem, NotificationPermission};
16use crate::window::WindowSize;
17use std::any::Any;
18use std::cell::{Cell, RefCell};
19use std::collections::HashMap;
20use std::fmt;
21use std::marker::PhantomData;
22use std::panic::Location;
23use std::rc::{Rc, Weak};
24use std::sync::atomic::{AtomicU64, Ordering};
25use std::sync::{Arc, Mutex, MutexGuard};
26
27use super::composer::RuntimeSignal;
28
29static NEXT_STATE_ID: AtomicU64 = AtomicU64::new(1);
32
33fn next_state_id() -> u64 {
34 NEXT_STATE_ID.fetch_add(1, Ordering::Relaxed)
35}
36
37fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
38 mutex.lock().unwrap_or_else(|poison| poison.into_inner())
39}
40
41pub struct SnapshotMutationPolicy<T> {
43 equivalent: fn(&T, &T) -> bool,
44}
45
46impl<T> Clone for SnapshotMutationPolicy<T> {
47 fn clone(&self) -> Self {
48 *self
49 }
50}
51
52impl<T> Copy for SnapshotMutationPolicy<T> {}
53
54pub fn structural_equality_policy<T: PartialEq>() -> SnapshotMutationPolicy<T> {
56 SnapshotMutationPolicy {
57 equivalent: |left, right| left == right,
58 }
59}
60
61pub fn never_equal_policy<T>() -> SnapshotMutationPolicy<T> {
63 SnapshotMutationPolicy {
64 equivalent: |_, _| false,
65 }
66}
67
68struct StateCell<T> {
69 id: u64,
70 value: Mutex<T>,
71 policy: SnapshotMutationPolicy<T>,
72 runtimes: Mutex<Vec<std::sync::Weak<RuntimeSignal>>>,
74}
75
76impl<T> StateCell<T> {
77 fn notify(&self) {
78 let mut told = false;
79 lock(&self.runtimes).retain(|runtime| match runtime.upgrade() {
80 Some(signal) => {
81 signal.push_dirty(self.id);
82 told = true;
83 true
84 }
85 None => false,
86 });
87 if told && !crate::boundary::in_host_call() {
90 crate::boundary::request_frame_from_worker();
91 }
92 }
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub(crate) struct StateKey {
102 index: u32,
103 generation: u32,
104}
105
106struct Entry {
107 generation: u32,
108 cell: Option<Arc<dyn Any + Send + Sync>>,
109}
110
111struct Arena {
112 entries: Vec<Entry>,
113 free: Vec<u32>,
114}
115
116static ARENA: Mutex<Arena> = Mutex::new(Arena {
119 entries: Vec::new(),
120 free: Vec::new(),
121});
122
123static WARNED: Mutex<Vec<StateKey>> = Mutex::new(Vec::new());
125
126fn allocate(cell: Arc<dyn Any + Send + Sync>) -> StateKey {
127 let mut arena = lock(&ARENA);
128 if let Some(index) = arena.free.pop() {
129 let entry = &mut arena.entries[index as usize];
130 entry.cell = Some(cell);
131 return StateKey {
132 index,
133 generation: entry.generation,
134 };
135 }
136 let index = arena.entries.len() as u32;
137 arena.entries.push(Entry {
138 generation: 0,
139 cell: Some(cell),
140 });
141 StateKey {
142 index,
143 generation: 0,
144 }
145}
146
147pub(crate) fn release_state(key: StateKey) -> Option<Box<dyn Any>> {
150 let mut arena = lock(&ARENA);
151 let entry = arena.entries.get_mut(key.index as usize)?;
152 if entry.generation != key.generation {
153 return None;
154 }
155 let cell = entry.cell.take()?;
156 entry.generation = entry.generation.wrapping_add(1);
157 arena.free.push(key.index);
158 Some(Box::new(cell))
159}
160
161thread_local! {
164 static PENDING: RefCell<Vec<StateKey>> = const { RefCell::new(Vec::new()) };
166 static SITES: RefCell<Vec<&'static Location<'static>>> = const { RefCell::new(Vec::new()) };
167}
168
169pub(crate) struct Capture {
172 mark: usize,
173}
174
175impl Capture {
176 pub(crate) fn begin(site: &'static Location<'static>) -> Self {
177 let mark = PENDING.with_borrow(Vec::len);
178 SITES.with_borrow_mut(|sites| sites.push(site));
179 Self { mark }
180 }
181
182 pub(crate) fn finish(self) -> Vec<StateKey> {
184 PENDING.with_borrow_mut(|pending| pending.split_off(self.mark.min(pending.len())))
185 }
186}
187
188impl Drop for Capture {
189 fn drop(&mut self) {
190 SITES.with_borrow_mut(|sites| {
191 sites.pop();
192 });
193 PENDING.with_borrow_mut(|pending| pending.truncate(self.mark));
194 }
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq)]
199pub enum StateError {
200 Released,
203}
204
205impl fmt::Display for StateError {
206 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
207 match self {
208 Self::Released => formatter.write_str(
209 "Released: the state left the composition with the remember call that made it",
210 ),
211 }
212 }
213}
214
215impl std::error::Error for StateError {}
216
217pub struct MutableState<T> {
238 key: StateKey,
239 site: &'static Location<'static>,
241 marker: PhantomData<fn() -> T>,
242}
243
244impl<T> Clone for MutableState<T> {
245 fn clone(&self) -> Self {
246 *self
247 }
248}
249
250impl<T> Copy for MutableState<T> {}
251
252impl<T> PartialEq for MutableState<T> {
253 fn eq(&self, other: &Self) -> bool {
254 self.key == other.key
255 }
256}
257
258impl<T> Eq for MutableState<T> {}
259
260impl<T> std::hash::Hash for MutableState<T> {
261 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
262 self.key.hash(state);
263 }
264}
265
266impl<T> fmt::Debug for MutableState<T> {
267 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
268 formatter
269 .debug_struct("MutableState")
270 .field("index", &self.key.index)
271 .field("generation", &self.key.generation)
272 .finish()
273 }
274}
275
276#[track_caller]
287pub fn mutable_state_of<T: PartialEq + Send + 'static>(value: T) -> MutableState<T> {
288 mutable_state_of_with_policy(value, structural_equality_policy())
289}
290
291#[track_caller]
293pub fn mutable_state_of_with_policy<T: Send + 'static>(
294 value: T,
295 policy: SnapshotMutationPolicy<T>,
296) -> MutableState<T> {
297 let here = Location::caller();
298 let cell: Arc<dyn Any + Send + Sync> = Arc::new(StateCell {
299 id: next_state_id(),
300 value: Mutex::new(value),
301 policy,
302 runtimes: Mutex::new(Vec::new()),
303 });
304 let key = allocate(cell);
305 let site = SITES.with_borrow(|sites| sites.last().copied());
306 if site.is_some() {
307 PENDING.with_borrow_mut(|pending| pending.push(key));
308 }
309 MutableState {
310 key,
311 site: site.unwrap_or(here),
312 marker: PhantomData,
313 }
314}
315
316impl<T: Send + 'static> MutableState<T> {
317 fn resolve(&self) -> Option<Arc<StateCell<T>>> {
318 let cell = {
319 let arena = lock(&ARENA);
320 let entry = arena.entries.get(self.key.index as usize)?;
321 if entry.generation != self.key.generation {
322 return None;
323 }
324 entry.cell.clone()?
325 };
326 cell.downcast::<StateCell<T>>().ok()
327 }
328
329 fn released(&self) -> ! {
330 panic!(
331 "Released: this MutableState was used after the remember call at {}:{} left the composition",
332 self.site.file(),
333 self.site.line()
334 )
335 }
336
337 fn warn_released(&self) {
338 if !cfg!(debug_assertions) {
339 return;
340 }
341 {
342 let mut warned = lock(&WARNED);
343 if warned.contains(&self.key) {
344 return;
345 }
346 warned.push(self.key);
347 }
348 eprintln!(
349 "compose-rust: a write to a released MutableState was ignored (made by the remember call at {}:{})",
350 self.site.file(),
351 self.site.line()
352 );
353 }
354
355 #[doc(hidden)]
358 pub fn released_warning_count(&self) -> usize {
359 lock(&WARNED).iter().filter(|key| **key == self.key).count()
360 }
361
362 pub fn is_released(&self) -> bool {
364 self.resolve().is_none()
365 }
366
367 pub fn get(&self) -> T
370 where
371 T: Clone,
372 {
373 self.try_get().unwrap_or_else(|_| self.released())
374 }
375
376 pub fn try_get(&self) -> Result<T, StateError>
379 where
380 T: Clone,
381 {
382 let cell = self.resolve().ok_or(StateError::Released)?;
383 Self::track(&cell);
384 let value = lock(&cell.value).clone();
385 Ok(value)
386 }
387
388 pub fn get_untracked(&self) -> T
391 where
392 T: Clone,
393 {
394 match self.resolve() {
395 Some(cell) => lock(&cell.value).clone(),
396 None => self.released(),
397 }
398 }
399
400 pub fn with<R>(&self, read: impl FnOnce(&T) -> R) -> R {
402 match self.resolve() {
403 Some(cell) => {
404 Self::track(&cell);
405 let value = lock(&cell.value);
406 read(&value)
407 }
408 None => self.released(),
409 }
410 }
411
412 pub fn set(&self, value: T) {
415 if self.try_set(value).is_err() {
416 self.warn_released();
417 }
418 }
419
420 pub fn try_set(&self, value: T) -> Result<(), StateError> {
423 let cell = self.resolve().ok_or(StateError::Released)?;
424 {
425 let mut current = lock(&cell.value);
426 if (cell.policy.equivalent)(¤t, &value) {
427 return Ok(());
428 }
429 *current = value;
430 }
431 cell.notify();
432 Ok(())
433 }
434
435 pub fn update(&self, change: impl FnOnce(&mut T)) {
439 if self.try_update(change).is_err() {
440 self.warn_released();
441 }
442 }
443
444 pub fn try_update(&self, change: impl FnOnce(&mut T)) -> Result<(), StateError> {
447 let cell = self.resolve().ok_or(StateError::Released)?;
448 change(&mut lock(&cell.value));
449 cell.notify();
450 Ok(())
451 }
452
453 pub fn as_state(&self) -> MutableState<T> {
455 *self
456 }
457
458 fn track(cell: &Arc<StateCell<T>>) {
459 let fresh = with_composer_if_idle(|composer| {
460 composer
461 .record_read(cell.id)
462 .then(|| Arc::clone(&composer.signal))
463 })
464 .flatten();
465 if let Some(signal) = fresh {
466 let mut runtimes = lock(&cell.runtimes);
467 if !runtimes
468 .iter()
469 .any(|known| std::ptr::eq(known.as_ptr(), Arc::as_ptr(&signal)))
470 {
471 runtimes.push(Arc::downgrade(&signal));
472 }
473 }
474 }
475}
476
477pub trait State<T> {
479 fn value(&self) -> T;
481}
482
483impl<T: Clone + Send + 'static> State<T> for MutableState<T> {
484 fn value(&self) -> T {
485 self.get()
486 }
487}
488
489impl<T: Clone + PartialEq + 'static> State<T> for DerivedState<T> {
490 fn value(&self) -> T {
491 self.get()
492 }
493}
494
495pub(crate) trait DerivedNode {
497 fn refresh(&self) -> bool;
499 fn mark_dirty(&self);
501}
502
503struct DerivedCell<T> {
504 id: u64,
505 compute: Box<dyn Fn() -> T>,
506 value: RefCell<Option<T>>,
507 dirty: Cell<bool>,
508}
509
510pub struct DerivedState<T> {
520 cell: Rc<DerivedCell<T>>,
521}
522
523impl<T> Clone for DerivedState<T> {
524 fn clone(&self) -> Self {
525 Self {
526 cell: Rc::clone(&self.cell),
527 }
528 }
529}
530
531impl<T> PartialEq for DerivedState<T> {
532 fn eq(&self, other: &Self) -> bool {
533 Rc::ptr_eq(&self.cell, &other.cell)
534 }
535}
536
537pub fn derived_state_of<T: Clone + PartialEq + 'static>(
544 calculation: impl Fn() -> T + 'static,
545) -> DerivedState<T> {
546 DerivedState {
547 cell: Rc::new(DerivedCell {
548 id: next_state_id(),
549 compute: Box::new(calculation),
550 value: RefCell::new(None),
551 dirty: Cell::new(true),
552 }),
553 }
554}
555
556impl<T: Clone + PartialEq + 'static> DerivedState<T> {
557 pub fn get(&self) -> T {
559 let in_runtime = with_composer_if_idle(|_| ()).is_some();
560 if !in_runtime {
561 return (self.cell.compute)();
564 }
565 if self.cell.dirty.get() || self.cell.value.borrow().is_none() {
566 self.recompute();
567 }
568 with_composer_if_idle(|composer| composer.record_read(self.cell.id));
569 self.cell
570 .value
571 .borrow()
572 .clone()
573 .expect("a derived state holds a value once computed")
574 }
575
576 fn recompute(&self) -> bool {
577 let id = self.cell.id;
578 let node: Rc<dyn DerivedNode> = self.cell.clone();
579 let weak = Rc::downgrade(&node);
580 with_composer(|composer| {
581 if let Some(deps) = composer.derived_deps.remove(&id) {
582 for dep in deps {
583 composer.unobserve(dep, Observer::Derived(id));
584 }
585 }
586 composer.ambient.deriveds.insert(id, weak);
587 composer.derived_stack.push(id);
588 });
589 let computed = (self.cell.compute)();
590 with_composer(|composer| {
591 composer.derived_stack.pop();
592 });
593 self.cell.dirty.set(false);
594 let mut stored = self.cell.value.borrow_mut();
595 let changed = stored.as_ref() != Some(&computed);
596 *stored = Some(computed);
597 changed
598 }
599}
600
601impl<T: Clone + PartialEq + 'static> DerivedNode for DerivedCell<T> {
602 fn refresh(&self) -> bool {
603 let state = DerivedState {
605 cell: rc_from_cell(self),
606 };
607 state.recompute()
608 }
609
610 fn mark_dirty(&self) {
611 self.dirty.set(true);
612 }
613}
614
615fn rc_from_cell<T>(cell: &DerivedCell<T>) -> Rc<DerivedCell<T>> {
619 let pointer = cell as *const DerivedCell<T>;
620 unsafe {
624 Rc::increment_strong_count(pointer);
625 Rc::from_raw(pointer)
626 }
627}
628
629pub(crate) struct Ambient {
632 pub(crate) window: MutableState<WindowSize>,
633 pub(crate) design: MutableState<DesignSystem>,
634 pub(crate) permission: MutableState<NotificationPermission>,
635 pub(crate) windows: HashMap<u32, WindowAmbient>,
638 pub(crate) deriveds: HashMap<u64, Weak<dyn DerivedNode>>,
639}
640
641pub(crate) struct WindowAmbient {
643 state: MutableState<WindowSize>,
644 _subscription: crate::window::WindowSubscription,
645}
646
647impl Drop for WindowAmbient {
648 fn drop(&mut self) {
649 drop(release_state(self.state.key));
650 }
651}
652
653impl Drop for Ambient {
654 fn drop(&mut self) {
655 let values = [
656 release_state(self.window.key),
657 release_state(self.design.key),
658 release_state(self.permission.key),
659 ];
660 drop(values);
661 }
662}
663
664impl Ambient {
665 pub(crate) fn new() -> Self {
666 Self {
667 window: mutable_state_of(crate::window::window_size()),
668 design: mutable_state_of(crate::design::design_system()),
669 permission: mutable_state_of(crate::notification::notification_permission()),
670 windows: HashMap::new(),
671 deriveds: HashMap::new(),
672 }
673 }
674}
675
676pub(crate) fn apply_state_changes() {
683 loop {
684 let Some(mut changed) = with_composer(|composer| {
685 let mut scratch = std::mem::take(&mut composer.dirty_scratch);
686 std::mem::swap(&mut *lock(&composer.signal.dirty), &mut scratch);
687 scratch
688 }) else {
689 return;
690 };
691 if changed.is_empty() {
692 with_composer(|composer| composer.dirty_scratch = changed);
693 return;
694 }
695 let mut index = 0;
696 while index < changed.len() {
697 let state = changed[index];
698 index += 1;
699 let observers = with_composer(|composer| {
700 composer.observers.get(&state).cloned().unwrap_or_default()
701 })
702 .unwrap_or_default();
703 for observer in observers {
704 match observer {
705 Observer::Scope(group, generation) => {
706 with_composer(|composer| composer.invalidate(group, generation));
707 }
708 Observer::Derived(derived) => {
709 let (node, watched) = with_composer(|composer| {
710 let node = composer
711 .ambient
712 .deriveds
713 .get(&derived)
714 .and_then(Weak::upgrade);
715 let watched = composer
716 .observers
717 .get(&derived)
718 .is_some_and(|list| !list.is_empty());
719 (node, watched)
720 })
721 .unwrap_or((None, false));
722 let Some(node) = node else {
723 continue;
724 };
725 if watched {
726 if node.refresh() && !changed.contains(&derived) {
727 changed.push(derived);
728 }
729 } else {
730 node.mark_dirty();
731 }
732 }
733 }
734 }
735 }
736 changed.clear();
737 with_composer(|composer| composer.dirty_scratch = changed);
738 }
739}
740
741pub fn current_window_size() -> WindowSize {
747 let window = crate::windows::current_window_id();
748 if window != 0 {
749 if let Some(state) = window_state(window) {
750 return state.get();
751 }
752 }
753 match with_composer_if_idle(|composer| composer.ambient.window) {
754 Some(state) => state.get(),
755 None => crate::window::window_size(),
756 }
757}
758
759fn window_state(window: u32) -> Option<MutableState<WindowSize>> {
761 with_composer_if_idle(|composer| {
762 if let Some(entry) = composer.ambient.windows.get(&window) {
763 return entry.state;
764 }
765 let state = mutable_state_of(crate::window::window_size_of(window));
766 PENDING.with_borrow_mut(|pending| pending.retain(|key| *key != state.key));
768 let follow = state;
769 let subscription = crate::window::subscribe_window(
770 window,
771 Arc::new(move || follow.set(crate::window::window_size_of(window))),
772 );
773 composer.ambient.windows.insert(
774 window,
775 WindowAmbient {
776 state,
777 _subscription: subscription,
778 },
779 );
780 state
781 })
782}
783
784pub fn current_design_system() -> DesignSystem {
790 match with_composer_if_idle(|composer| composer.ambient.design) {
791 Some(state) => state.get(),
792 None => crate::design::design_system(),
793 }
794}
795
796pub fn current_notification_permission() -> NotificationPermission {
798 match with_composer_if_idle(|composer| composer.ambient.permission) {
799 Some(state) => state.get(),
800 None => crate::notification::notification_permission(),
801 }
802}
803
804const NODE_SIZE: u64 = super::composer::call_site("compose_rust::remember_node_size", 0);
805const NOTIFICATION_ACTIVATED: u64 =
806 super::composer::call_site("compose_rust::on_notification_activated", 0);
807
808struct NodeSizeSlot {
811 token: u32,
812 state: MutableState<WindowSize>,
813 _subscription: crate::window::NodeSizeSubscription,
814}
815
816pub fn remember_node_size() -> crate::window::NodeSize {
823 let _group = super::group_guard(NODE_SIZE);
824 let found = with_composer_if_idle(|composer| {
825 if !composer.composing() {
826 return None;
827 }
828 if let Some(slot) = composer.slot_mut::<NodeSizeSlot>() {
829 return Some((slot.token, slot.state));
830 }
831 let token = crate::window::next_node_token();
832 let capture = Capture::begin(Location::caller());
833 let state = mutable_state_of(crate::window::node_size(token));
834 let owned = capture.finish();
835 let follow = state;
836 let subscription = crate::window::subscribe_node(
837 token,
838 std::sync::Arc::new(move || follow.set(crate::window::node_size(token))),
839 );
840 composer.remember_new_owning(
841 NodeSizeSlot {
842 token,
843 state,
844 _subscription: subscription,
845 },
846 owned,
847 );
848 Some((token, state))
849 })
850 .flatten();
851 match found {
852 Some((token, state)) => {
853 let _ = state.get();
856 crate::window::NodeSize::of(token)
857 }
858 None => crate::window::NodeSize::of(0),
859 }
860}
861
862type ActivationHandler = Rc<RefCell<Box<dyn FnMut(crate::NotificationActivation)>>>;
864
865struct ActivationSlot {
868 handler: ActivationHandler,
869 _subscription: crate::notification::ActivationSubscription,
870}
871
872pub fn on_notification_activated(handler: impl FnMut(crate::NotificationActivation) + 'static) {
878 let _group = super::group_guard(NOTIFICATION_ACTIVATED);
879 let handler: Box<dyn FnMut(crate::NotificationActivation)> = Box::new(handler);
880 let mut handler = Some(handler);
881 let old = with_composer_if_idle(|composer| {
882 if !composer.composing() {
883 return None;
884 }
885 if let Some(slot) = composer.slot_mut::<ActivationSlot>() {
886 let new = handler.take().expect("the handler is installed once");
887 return Some(std::mem::replace(&mut *slot.handler.borrow_mut(), new));
888 }
889 let shared = Rc::new(RefCell::new(
890 handler.take().expect("the handler is installed once"),
891 ));
892 let forward = Rc::clone(&shared);
893 let subscription = crate::notification::on_activation(Rc::new(
894 move |activation: crate::NotificationActivation| (*forward.borrow_mut())(activation),
895 ));
896 composer.remember_new(ActivationSlot {
897 handler: shared,
898 _subscription: subscription,
899 });
900 None
901 })
902 .flatten();
903 drop(old);
905}
906
907const WINDOW_REQUESTED: u64 = super::composer::call_site("compose_rust::on_window_requested", 0);
908const APPLICATION_REOPEN: u64 =
909 super::composer::call_site("compose_rust::on_application_reopen", 0);
910
911type SharedHandler<A> = Rc<RefCell<Box<dyn FnMut(A)>>>;
914
915struct HandlerSlot<A: 'static> {
916 handler: SharedHandler<A>,
917 _subscription: Box<dyn std::any::Any>,
918}
919
920fn install_handler<A: 'static>(
924 key: u64,
925 handler: Box<dyn FnMut(A)>,
926 subscribe: impl FnOnce(Rc<dyn Fn(A)>) -> Box<dyn std::any::Any>,
927) {
928 let _group = super::group_guard(key);
929 let mut handler = Some(handler);
930 let mut subscribe = Some(subscribe);
931 let old = with_composer_if_idle(|composer| {
932 if !composer.composing() {
933 return None;
934 }
935 if let Some(slot) = composer.slot_mut::<HandlerSlot<A>>() {
936 let new = handler.take().expect("the handler is installed once");
937 return Some(std::mem::replace(&mut *slot.handler.borrow_mut(), new));
938 }
939 let shared = Rc::new(RefCell::new(
940 handler.take().expect("the handler is installed once"),
941 ));
942 let forward = Rc::clone(&shared);
943 let subscription = (subscribe.take().expect("the handler is installed once"))(Rc::new(
944 move |argument: A| (*forward.borrow_mut())(argument),
945 ));
946 composer.remember_new(HandlerSlot {
947 handler: shared,
948 _subscription: subscription,
949 });
950 None
951 })
952 .flatten();
953 drop(old);
955}
956
957pub fn on_window_requested(handler: impl FnMut(crate::windows::WindowRequest) + 'static) {
962 install_handler::<crate::windows::WindowRequest>(
963 WINDOW_REQUESTED,
964 Box::new(handler),
965 |forward| Box::new(crate::windows::on_window_request(forward)),
966 );
967}
968
969pub fn on_application_reopen(handler: impl FnMut() + 'static) {
972 let mut handler = handler;
973 install_handler::<()>(
974 APPLICATION_REOPEN,
975 Box::new(move |(): ()| handler()),
976 |forward| {
977 Box::new(crate::windows::on_application_reopen_handler(Rc::new(
978 move || forward(()),
979 )))
980 },
981 );
982}