polyhorn_android/raw/
compositor.rs

1use polyhorn_android_sys::Runnable;
2use polyhorn_core::{Command, Composition};
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::sync::{Arc, Mutex};
5
6use super::{Environment, OpaqueContainer, Platform};
7
8/// Concrete implementation of a compositor that is responsible for adding and
9/// removing native views from the native view hierarchy based on the virtual
10/// representation within Polyhorn.
11#[derive(Clone)]
12pub struct Compositor {
13    environment: Environment,
14    buffer: Arc<Mutex<Composition<Platform>>>,
15    counter: Arc<AtomicUsize>,
16}
17
18impl Compositor {
19    /// Returns a new compositor with the given shared layouter.
20    pub fn new(environment: Environment) -> Compositor {
21        Compositor {
22            environment,
23            buffer: Arc::new(Default::default()),
24            counter: Arc::new(AtomicUsize::default()),
25        }
26    }
27
28    fn next_id(&mut self) -> ContainerID {
29        let id = self.counter.fetch_add(1, Ordering::Relaxed);
30        ContainerID(id)
31    }
32
33    pub(crate) fn track(&mut self, container: OpaqueContainer) -> ContainerID {
34        let id = self.next_id();
35        self.buffer.lock().unwrap().insert(id, container);
36        id
37    }
38}
39
40impl polyhorn_core::Compositor<Platform> for Compositor {
41    fn buffer(&self) -> CommandBuffer {
42        CommandBuffer {
43            compositor: self.clone(),
44            commands: vec![],
45        }
46    }
47}
48
49/// An opaque ID for containers that can be shared between threads.
50#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
51pub struct ContainerID(usize);
52
53/// Concrete implementation of a command buffer that can buffer commands before
54/// committing them to the compositor.
55pub struct CommandBuffer {
56    compositor: Compositor,
57    commands: Vec<Command<Platform>>,
58}
59
60impl polyhorn_core::CommandBuffer<Platform> for CommandBuffer {
61    fn mount<F>(&mut self, parent_id: ContainerID, initializer: F) -> ContainerID
62    where
63        F: FnOnce(&mut OpaqueContainer, &mut Environment) -> OpaqueContainer + Send + 'static,
64    {
65        let id = self.compositor.next_id();
66        self.commands
67            .push(Command::Mount(id, parent_id, Box::new(initializer)));
68        id
69    }
70
71    fn mutate<F>(&mut self, ids: &[ContainerID], mutator: F)
72    where
73        F: FnOnce(&mut [&mut OpaqueContainer], &mut Environment) + Send + 'static,
74    {
75        self.commands
76            .push(Command::Mutate(ids.to_owned(), Box::new(mutator)));
77    }
78
79    fn unmount(&mut self, id: ContainerID) {
80        self.commands.push(Command::Unmount(id));
81    }
82
83    fn layout(&mut self) {
84        self.mutate(&[], |_, environment| {
85            environment.layout_tree().write().unwrap().recompute_roots();
86        });
87    }
88
89    fn commit(mut self) {
90        let commands = std::mem::take(&mut self.commands);
91
92        let activity = self.compositor.environment.activity().clone();
93        let layout_tree = self.compositor.environment.layout_tree().clone();
94        let state = self.compositor.buffer.clone();
95
96        Runnable::new(&self.compositor.environment.env(), move |env| {
97            let mut environment = Environment::new(
98                activity,
99                unsafe { env.clone().prolong_lifetime() },
100                layout_tree.clone(),
101            );
102
103            let mut state = state.lock().unwrap();
104
105            // Apply each command to this state.
106            for command in commands {
107                state.process(&mut environment, command);
108            }
109
110            let mut layout_tree = layout_tree.write().unwrap();
111            layout_tree.recompute_roots();
112        })
113        .queue(&self.compositor.environment.env());
114    }
115}