1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* Copyright (C) 2023 Purism SPC
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

/*! The camera tracking list.
 * It runs in a separate thread, with its own event loop. It receives udev events to update the list on one side, and the request to return the current list on the other side.
 * It communicates only using channels.
 * TODO: the user will want to react to new cameras. Give them some way to do it.
 */

use crate::actors::watcher_udev;
use crate::pipelines;
use crate::pipelines::{PIPELINES, Builder, UnacquiredCamera, CameraInfo};
use std::io;
use std::sync::mpsc;
use std::thread;

/// The information and handler to create a new camera
#[derive(Debug, Clone)]
pub struct CreationKit {
    /// Information about the camera
    pub info: CameraInfo,
    /// The function to build the camera handle
    pub builder: Builder,
}

/// Returns ID and data
pub type Cameras = Vec<CreationKit>;

#[derive(Debug)]
pub enum Message {
    DeviceChange(watcher_udev::Event),
    ShowCameras,
    Stop,
}

impl From<watcher_udev::Event> for Message {
    fn from(e: watcher_udev::Event) -> Self {
        Message::DeviceChange(e)
    }
}

/// Stores camera present on the system
pub struct Handle {
    sender: mpsc::SyncSender<Message>,
    thread: Option<thread::JoinHandle<Result<(), mpsc::RecvError>>>,
    cam_receiver: mpsc::Receiver<Cameras>,
}

impl Handle {
    /// Requests the handler thread to stop and waits.
    /// Call this to ensure that the thread is stopped. Dropping Handle requests the stopping asynchronously and doesn't ensure success.
    pub fn stop(mut self)
    -> Result<
        Result<(), mpsc::RecvError>,
        (Self, &'static str)
    > {
        match self.sender.send(Message::Stop) {
            Ok(()) => Ok({
                if let Some(thread) = self.thread.take() {
                    thread.join().unwrap()
                } else {
                    Ok(())
                }
            }),
            Err(_) => {
                if self.thread.is_some()
                    && self.thread.as_ref().map(|t| t.is_finished()).unwrap() 
                {
                    Ok(Ok(()))
                } else {
                    Err((self, "Failed to contact thread"))
                }
            },
        }
    }
    
    fn sender(&self) -> &mpsc::SyncSender<Message> {
        &self.sender
    }
    
    pub fn cameras(&self) -> Cameras {
        self.sender.send(Message::ShowCameras).unwrap();
        self.cam_receiver.recv().unwrap()
    }
    
    // TODO: move to manager
    /// Returns the camera with this ID, or None if one was not found.
    pub fn create(&self, needed_id: &str)
        -> Option<Result<UnacquiredCamera, pipelines::Error>>
    {
        self.cameras().into_iter()
            .find(|CreationKit { info: CameraInfo { id, .. }, ..}| id == needed_id)
            .map(|CreationKit { info, builder }| builder(info))
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        let _ = self.sender.send(Message::Stop);
    }
}

fn spawn_handle() -> Handle {
    let (sender, receiver) = mpsc::sync_channel(1);
    
    let (cam_sender, cam_receiver) = mpsc::sync_channel(1);

    let thread = Some(thread::spawn(move || run(receiver, cam_sender)));
    Handle { sender, thread, cam_receiver }
}

/// Tracks cameras present on the system
pub struct Tracker {
    handle: Handle,
    /// The supporting udev watcher thread.
    watcher: watcher_udev::Watcher,
}

use std::ops::Deref;

impl Deref for Tracker {
    type Target = Handle;
    fn deref(&self) -> &Self::Target {
        &self.handle
    }
}

impl Tracker {
    /// Ensures that the extra threads are stopped
    pub fn stop(self) -> Result<(), ()> {
        // TODO: what to do if one doesn't stop?
        let _ = self.handle.stop().map_err(|_| ())?;
        let _ = self.watcher.stop().map_err(|_| ())?;
        Ok(())
    }
}

pub fn spawn() -> Result<Tracker, io::Error> {
    let cameras_list = spawn_handle();
    let sender = cameras_list.sender().clone();
    let watcher = watcher_udev::Watcher::spawn(
        move |ev| { 
            let _ = sender.send(ev.into());
        }
    )?;
    Ok(Tracker {
        handle: cameras_list,
        watcher,
    })
}

/// The main event loop forwarding messages from the udev watcher to the camera list.
fn run(
    receiver: mpsc::Receiver<Message>,
    sender: mpsc::SyncSender<Cameras>,
) -> Result<(), mpsc::RecvError> {
    // This is the actual storage of information about attached cameras
    let mut cameras: Vec<CreationKit> = Vec::new();
    let mut present_devices = Vec::new();
    loop {
        match receiver.recv()? {
            Message::Stop => { break },
            Message::DeviceChange(watcher_udev::Event {
                kind: watcher_udev::EventKind::Added,
                device,
            }) => {
                present_devices.push(device.clone());
                let matching_cam = cameras.iter()
                    .find(|CreationKit { info, .. }| info.is_for_device(&device));
                if let None = matching_cam {
                    if let Some(camera) = PIPELINES.iter().find_map(|check| check(&device)) {
                        cameras.push(camera);
                    }
                }
            },
            Message::DeviceChange(watcher_udev::Event {
                kind: watcher_udev::EventKind::Removed,
                device,
            }) => {
                present_devices.iter()
                    .position(|d| *d == device)
                    .map(|i| present_devices.remove(i));
                cameras.iter()
                    .position(|CreationKit { info, ..}| info.is_for_device(&device))
                    .map(|i| cameras.remove(i));
            },
            Message::ShowCameras => {
                let _ = sender.send(
                    cameras.iter()
                        .map(|v| v.clone())
                        .collect()
                );
            },
        }
    }
    Ok(())
}