camera/
lib.rs

1// SPDX-FileCopyrightText: 2023 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#[cfg(not(keyos))]
5mod hosted;
6pub mod messages;
7
8use std::time::Duration;
9
10#[cfg(not(keyos))]
11pub use hosted::*;
12use server::{CheckedConn, CheckedPermissions, MessageAllowed};
13
14use crate::messages::*;
15
16// Center the camera vertically with the UI's viewfinder image
17pub const CAMERA_WIDTH: usize = 480;
18pub const CAMERA_HEIGHT: usize = 480;
19
20// Duplicating this constant here to avoid an unnecessary dependency on gui-server-api
21const SCREEN_HEIGHT: usize = 800;
22
23// Margin on the top and bottom of the camera framebuffer so we can crop a SCREEN_HEIGHT
24// slice out of it at any vertical offset.
25pub const CAMERA_MARGIN: usize = SCREEN_HEIGHT - CAMERA_HEIGHT;
26
27#[cfg(keyos)]
28pub const CAMERA_BYTES_PER_PX: usize = 2; // RGB565 (2 bytes) is used on hardware
29#[cfg(not(keyos))]
30pub const CAMERA_BYTES_PER_PX: usize = 4;
31pub const CAMERA_FB_SIZE_BYTES: usize =
32    CAMERA_WIDTH * (CAMERA_HEIGHT + CAMERA_MARGIN * 2) * CAMERA_BYTES_PER_PX;
33
34pub const SERVER_NAME: &str = "os/camera";
35
36#[macro_export]
37macro_rules! use_api {
38    () => {
39        mod camera_permissions {
40            use camera::messages::*;
41            #[derive(Clone, Default, server::Permissions)]
42            #[server_name = "os/camera"]
43            pub struct CameraPermissions;
44        }
45        type CameraApi = camera::CameraApi<camera_permissions::CameraPermissions>;
46    };
47}
48
49#[derive(Default)]
50pub struct CameraApi<P: CheckedPermissions>(CheckedConn<P>);
51
52impl<P: CheckedPermissions> CameraApi<P> {
53    pub fn try_new_with_timeout(timeout: Duration) -> Option<Self> {
54        CheckedConn::try_connect_with_timeout(timeout).map(Self)
55    }
56
57    /// Start requesting camera frames. Cheap because the frames are not actually sent,
58    /// but mirrored on the client side. The event is sent when a new frame is available.
59    pub fn subscribe<S>(&self, context: &mut server::ServerContext<S>) -> Result<(), SubscriptionError>
60    where
61        S: server::Server + server::ScalarEventHandler<Frame>,
62        P: MessageAllowed<Subscribe>,
63    {
64        self.0.subscribe_scalar(Subscribe, context)
65    }
66
67    /// Enable the use of the camera. Intended to be used by the control center
68    pub fn set_enabled(&self, enabled: bool)
69    where
70        P: MessageAllowed<SetEnabled>,
71    {
72        self.0.send_scalar(SetEnabled(enabled));
73    }
74
75    /// Notify the app that the camera image is visible on the screen.
76    /// Intended to be used by the GUI server
77    pub fn notify_visible(&self, visible: bool)
78    where
79        P: MessageAllowed<NotifyVisible>,
80    {
81        self.0.send_scalar(NotifyVisible(visible));
82    }
83
84    pub fn is_enabled(&self) -> bool
85    where
86        P: MessageAllowed<IsEnabled>,
87    {
88        self.0.send_blocking_scalar(IsEnabled)
89    }
90
91    pub fn is_in_use(&self) -> bool
92    where
93        P: MessageAllowed<IsInUse>,
94    {
95        self.0.send_blocking_scalar(IsInUse)
96    }
97
98    /// Get current camera parameters
99    pub fn get_params(&self) -> Result<CameraParams, xous::Error>
100    where
101        P: MessageAllowed<GetParams>,
102    {
103        self.0.try_send_blocking_archive(GetParams).map_err(From::from)
104    }
105
106    /// Set camera parameters
107    pub fn set_params(&self, params: CameraParams) -> Result<(), xous::Error>
108    where
109        P: MessageAllowed<SetParams>,
110    {
111        self.0.try_send_archive(SetParams(params))?;
112        Ok(())
113    }
114}
115
116#[cfg(keyos)]
117pub struct Frame(xous::MemoryRange);
118#[cfg(keyos)]
119server::wrapped_scalar!(Frame);
120
121#[cfg(keyos)]
122impl Frame {
123    pub fn new(range: xous::MemoryRange) -> Self { Self(range) }
124
125    pub fn padded_range(&self) -> xous::MemoryRange { self.0 }
126
127    pub fn content(&self) -> xous::MemoryRange {
128        self.0
129            .subrange(
130                CAMERA_WIDTH * CAMERA_MARGIN * CAMERA_BYTES_PER_PX,
131                CAMERA_WIDTH * CAMERA_HEIGHT * CAMERA_BYTES_PER_PX,
132            )
133            .unwrap()
134    }
135}
136
137#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
138pub enum SubscriptionError {
139    OutOfMemory,
140    Other,
141}