Browse Source

WIP camera service overhaul

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
45468d3aa5
  1. 3
      ctru-rs/examples/buttons.rs
  2. 82
      ctru-rs/examples/camera-image.rs
  3. 690
      ctru-rs/src/services/cam.rs
  4. 29
      ctru-rs/src/services/hid.rs

3
ctru-rs/examples/buttons.rs

@ -25,12 +25,11 @@ fn main() {
// Get information about which keys were held down on this frame. // Get information about which keys were held down on this frame.
let keys = hid.keys_held(); let keys = hid.keys_held();
// Print the status of the 2 sliders. // Print the status of the volume slider.
println!( println!(
"\x1b[20;0HVolume slider: {} ", "\x1b[20;0HVolume slider: {} ",
hid.slider_volume() hid.slider_volume()
); );
println!("\x1b[21;0H3D slider: {} ", hid.slider_3d());
// We only want to print when the keys we're holding now are different // We only want to print when the keys we're holding now are different
// from what they were on the previous frame. // from what they were on the previous frame.

82
ctru-rs/examples/camera-image.rs

@ -3,17 +3,16 @@
//! This example demonstrates how to use the built-in cameras to take a picture and display it to the screen. //! This example demonstrates how to use the built-in cameras to take a picture and display it to the screen.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::cam::{Cam, Camera, OutputFormat, ShutterSound, ViewSize}; use ctru::services::cam::{
Cam, Camera, OutputFormat, ShutterSound, Trimming, ViewSize, WhiteBalance,
};
use ctru::services::gfx::{Flush, Screen, Swap}; use ctru::services::gfx::{Flush, Screen, Swap};
use ctru::services::gspgpu::FramebufferFormat; use ctru::services::gspgpu::FramebufferFormat;
use std::time::Duration; use std::time::Duration;
const WIDTH: usize = 400; const WIDTH: usize = 200;
const HEIGHT: usize = 240; const HEIGHT: usize = 100;
// The screen size is the width and height multiplied by 2 (RGB565 store pixels in 2 bytes).
const BUF_SIZE: usize = WIDTH * HEIGHT * 2;
const WAIT_TIMEOUT: Duration = Duration::from_millis(300); const WAIT_TIMEOUT: Duration = Duration::from_millis(300);
@ -35,30 +34,35 @@ fn main() {
let mut cam = Cam::new().expect("Failed to initialize CAM service."); let mut cam = Cam::new().expect("Failed to initialize CAM service.");
// Camera setup. // Camera setup.
{ let camera = &mut cam.outer_right_cam;
let camera = &mut cam.outer_right_cam;
camera
camera .set_view_size(ViewSize::TopLCD)
.set_view_size(ViewSize::TopLCD) .expect("Failed to set camera size");
.expect("Failed to set camera size"); camera
camera .set_output_format(OutputFormat::Rgb565)
.set_output_format(OutputFormat::Rgb565) .expect("Failed to set camera output format");
.expect("Failed to set camera output format"); camera
camera .set_noise_filter(true)
.set_noise_filter(true) .expect("Failed to enable noise filter");
.expect("Failed to enable noise filter"); camera
camera .set_auto_exposure(true)
.set_auto_exposure(true) .expect("Failed to enable auto exposure");
.expect("Failed to enable auto exposure"); camera
camera .set_white_balance(WhiteBalance::Auto)
.set_auto_white_balance(true) .expect("Failed to enable auto white balance");
.expect("Failed to enable auto white balance"); camera
camera .set_trimming(Trimming::Centered {
.set_trimming(false) width: WIDTH as i16,
.expect("Failed to disable trimming"); height: HEIGHT as i16,
} })
.expect("Failed to disable trimming");
let mut buf = vec![0u8; BUF_SIZE];
// We don't intend on making any other modifications to the camera, so this size should be enough.
let len = camera
.max_byte_count()
.expect("could not retrieve max image buffer size");
let mut buf = vec![0u8; len];
println!("\nPress R to take a new picture"); println!("\nPress R to take a new picture");
println!("Press Start to exit"); println!("Press Start to exit");
@ -79,20 +83,24 @@ fn main() {
// Take a picture and write it to the buffer. // Take a picture and write it to the buffer.
camera camera
.take_picture( .take_picture(&mut buf, WAIT_TIMEOUT)
&mut buf,
WIDTH.try_into().unwrap(),
HEIGHT.try_into().unwrap(),
WAIT_TIMEOUT,
)
.expect("Failed to take picture"); .expect("Failed to take picture");
let image_size = camera
.final_image_size()
.expect("could not retrieve final image size");
// Play the normal shutter sound. // Play the normal shutter sound.
cam.play_shutter_sound(ShutterSound::Normal) cam.play_shutter_sound(ShutterSound::Normal)
.expect("Failed to play shutter sound"); .expect("Failed to play shutter sound");
// Rotate the image and correctly display it on the screen. // Rotate the image and correctly display it on the screen.
rotate_image_to_screen(&buf, top_screen.raw_framebuffer().ptr, WIDTH, HEIGHT); rotate_image_to_screen(
&buf,
top_screen.raw_framebuffer().ptr,
image_size.0 as usize,
image_size.1 as usize,
);
// We will only flush and swap the "camera" screen, since the other screen is handled by the `Console`. // We will only flush and swap the "camera" screen, since the other screen is handled by the `Console`.
top_screen.flush_buffers(); top_screen.flush_buffers();

690
ctru-rs/src/services/cam.rs

@ -6,12 +6,17 @@
use crate::error::{Error, ResultCode}; use crate::error::{Error, ResultCode};
use crate::services::gspgpu::FramebufferFormat; use crate::services::gspgpu::FramebufferFormat;
use crate::services::ServiceReference;
use ctru_sys::Handle; use ctru_sys::Handle;
use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
static CAM_ACTIVE: Mutex<()> = Mutex::new(());
/// Handle to the Camera service. /// Handle to the Camera service.
#[non_exhaustive]
pub struct Cam { pub struct Cam {
_service_handler: ServiceReference,
/// Inside-facing camera. /// Inside-facing camera.
pub inner_cam: InwardCam, pub inner_cam: InwardCam,
/// Outside-facing right camera. /// Outside-facing right camera.
@ -224,33 +229,25 @@ pub enum ShutterSound {
MovieEnd = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE_END, MovieEnd = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE_END,
} }
/// Parameters to handle image trimming. /// Configuration to handle image trimming.
///
/// See [`Camera::set_trimming_params()`] to learn how to use this.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TrimmingParams { pub enum Trimming {
x_start: i16, /// Trimming configuration based on absolute coordinates of the image.
y_start: i16, Absolute {
x_end: i16, /// Top-left corner coordinates (x,y) of the trimmed area.
y_end: i16, top_left: (i16, i16),
} /// Bottom-right corner coordinates (x,y) of the trimmed area.
bottom_right: (i16, i16),
impl TrimmingParams { },
/// Creates a new [`TrimmingParams`] and guarantees the start coordinates are less than or /// Trimming configuration relatively to the center of the image.
/// equal to the end coordinates. Centered {
/// /// Width of the trimmed area.
/// # Panics width: i16,
/// /// Height of the trimmed area.
/// This function panics if the start coordinates are larger than the end coordinates (for each axis). height: i16,
pub fn new(x_start: i16, y_start: i16, x_end: i16, y_end: i16) -> TrimmingParams { },
assert!(x_start <= x_end && y_start <= y_end); /// Trimming disabled.
Self { Off,
x_start,
y_start,
x_end,
y_end,
}
}
} }
/// Data used by the camera to calibrate image quality for a single camera. /// Data used by the camera to calibrate image quality for a single camera.
@ -268,39 +265,33 @@ pub struct StereoCameraCalibrationData(pub ctru_sys::CAMU_StereoCameraCalibratio
/// ///
/// Usually used for selfies. /// Usually used for selfies.
#[non_exhaustive] #[non_exhaustive]
pub struct InwardCam; pub struct InwardCam {
view_size: ViewSize,
impl Camera for InwardCam { trimming: Trimming,
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1
}
} }
/// Right-side outward camera representation. /// Right-side outward camera representation.
#[non_exhaustive] #[non_exhaustive]
pub struct OutwardRightCam; pub struct OutwardRightCam {
view_size: ViewSize,
impl Camera for OutwardRightCam { trimming: Trimming,
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1
}
} }
/// Left-side outward camera representation. /// Left-side outward camera representation.
#[non_exhaustive] #[non_exhaustive]
pub struct OutwardLeftCam; pub struct OutwardLeftCam {
view_size: ViewSize,
impl Camera for OutwardLeftCam { trimming: Trimming,
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT2
}
} }
/// Both outer cameras combined. /// Both outer cameras combined.
/// ///
/// Usually used for 3D photos. /// Usually used for 3D photos.
#[non_exhaustive] #[non_exhaustive]
pub struct BothOutwardCam; pub struct BothOutwardCam {
view_size: ViewSize,
trimming: Trimming,
}
impl BothOutwardCam { impl BothOutwardCam {
/// Set whether to enable or disable brightness synchronization between the two cameras. /// Set whether to enable or disable brightness synchronization between the two cameras.
@ -318,6 +309,148 @@ impl BothOutwardCam {
} }
} }
impl Camera for InwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1
}
fn view_size(&self) -> ViewSize {
self.view_size
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.view_size = size;
self.set_trimming(Trimming::Off);
Ok(())
}
fn trimming(&self) -> Trimming {
self.trimming
}
fn set_trimming(&mut self, trimming: Trimming) {
match trimming {
Trimming::Absolute {
top_left,
bottom_right,
} => unsafe {
// Top left corner is "before" bottom right corner.
assert!(top_left.0 <= bottom_right.0 && top_left.1 <= bottom_right.1);
// All coordinates are positive.
assert!(
top_left.0 >= 0
&& top_left.1 >= 0
&& bottom_right.0 >= 0
&& bottom_right.1 >= 0
);
// All coordinates are within the view.
assert!(
top_left.0 < view_size.0
&& bottom_right.0 < view_size.0
&& top_left.1 < view_size.1
&& bottom_right.1 < view_size.1
);
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParams(
self.port_as_raw(),
top_left.0,
top_left.1,
bottom_right.0,
bottom_right.1,
))?;
Ok(())
},
Trimming::Centered { width, height } => unsafe {
let view_size: (i16, i16) = self.view_size().into();
// Trim sizes are positive.
assert!(width >= 0 && height >= 0);
// Trim sizes are within the view.
assert!(width <= view_size.0 && height <= view_size.1);
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter(
self.port_as_raw(),
width,
height,
view_size.0,
view_size.1,
))?;
Ok(())
},
Trimming::Off => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
Ok(())
},
}
}
}
impl Camera for OutwardRightCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1
}
fn view_size(&self) -> ViewSize {
self.view_size
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.view_size = size;
self.set_trimming(Trimming::Off)?;
Ok(())
}
}
impl Camera for OutwardLeftCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT2
}
fn view_size(&self) -> ViewSize {
self.view_size
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.view_size = size;
self.set_trimming(Trimming::Off)?;
Ok(())
}
}
impl Camera for BothOutwardCam { impl Camera for BothOutwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ { fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1_OUT2 ctru_sys::SELECT_OUT1_OUT2
@ -326,14 +459,41 @@ impl Camera for BothOutwardCam {
fn port_as_raw(&self) -> ctru_sys::u32_ { fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_BOTH ctru_sys::PORT_BOTH
} }
fn view_size(&self) -> ViewSize {
self.view_size
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.view_size = size;
self.set_trimming(Trimming::Off)?;
Ok(())
}
} }
/// Generic functionality common to all cameras. /// Generic functionality common to all cameras.
// TODO: Change "set true/set parameters" scheme (classic of C code) into a single "set parameter" scheme using enums. This is valid for stuff such as [`TrimmingParams`]
pub trait Camera { pub trait Camera {
/// Returns the raw value of the selected camera. /// Returns the raw value of the selected camera.
fn camera_as_raw(&self) -> ctru_sys::u32_; fn camera_as_raw(&self) -> ctru_sys::u32_;
/// Returns view size of the selected camera.
///
/// # Notes
///
/// This view is the full resolution at which the camera will take the photo.
/// If you are interested in the final image dimension, after all processing and modifications, have a look at [`Camera::final_image_size()`].
fn view_size(&self) -> ViewSize;
/// Returns the raw port of the selected camera. /// Returns the raw port of the selected camera.
fn port_as_raw(&self) -> ctru_sys::u32_ { fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_CAM1 ctru_sys::PORT_CAM1
@ -367,7 +527,7 @@ pub trait Camera {
} }
} }
/// Returns the maximum amount of transfer bytes based on the view size, trimming, and other /// Returns the maximum amount of bytes the final image will occupy based on the view size, trimming, pixel depth and other
/// modifications set to the camera. /// modifications set to the camera.
/// ///
/// # Example /// # Example
@ -381,35 +541,81 @@ pub trait Camera {
/// ///
/// let inward = &cam.inner_cam; /// let inward = &cam.inner_cam;
/// ///
/// // Inward cam is not busy since it is not being used. /// let transfer_count = inward.max_byte_count();
/// let transfer_count = inward.transfer_byte_count();
/// # /// #
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[doc(alias = "CAMU_GetTransferBytes")] #[doc(alias = "CAMU_GetTransferBytes")]
fn transfer_byte_count(&self) -> crate::Result<u32> { fn max_byte_count(&self) -> crate::Result<usize> {
unsafe { let size = self.final_image_size()?;
let mut transfer_bytes = 0;
ResultCode(ctru_sys::CAMU_GetTransferBytes( let mut res: usize = (size.0 as usize * size.1 as usize) * std::mem::size_of::<i16>();
&mut transfer_bytes,
self.port_as_raw(), // If we are taking a picture using both outwards cameras, we need to expect 2 images, rather than just 1
))?; if self.port_as_raw() == ctru_sys::PORT_BOTH {
Ok(transfer_bytes) res = res * 2;
} }
Ok(res)
} }
/// Set whether or not the camera should trim the image. /// Returns the dimensions of the final image based on the view size, trimming and other
/// modifications set to the camera.
/// ///
/// [`TrimmingParams`] can be set via [`Camera::set_trimming_params`]. /// # Example
#[doc(alias = "CAMU_SetTrimming")] ///
fn set_trimming(&mut self, enabled: bool) -> crate::Result<()> { /// ```no_run
unsafe { /// # use std::error::Error;
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), enabled))?; /// # fn main() -> Result<(), Box<dyn Error>> {
Ok(()) /// #
/// use ctru::services::cam::{Cam, Camera};
/// let cam = Cam::new()?;
///
/// let mut inward = &cam.inner_cam;
///
/// inward.set_trimming(Trimming::Centered {
/// width: 100,
/// height: 100,
/// });
///
/// // This result will take into account the trimming.
/// let final_resolution = inward.final_image_size();
/// #
/// # Ok(())
/// # }
/// ```
fn final_image_size(&self) -> crate::Result<(i16, i16)> {
match self.is_trimming_enabled()? {
// Take trimming into account
true => {
// This request should never fail, at least not under safe `ctru-rs` usage.
let trimming = self.trimming_configuration()?;
let Trimming::Absolute {
top_left,
bottom_right,
} = trimming
else {
unreachable!()
};
let width = bottom_right.0 - top_left.0;
let height = bottom_right.1 - top_left.1;
Ok((width, height))
}
// Simply use the full view size.
false => Ok(self.view_size().into()),
} }
} }
fn trimming(&self) -> Trimming;
/// Set trimming bounds to trim the camera photo.
#[doc(alias = "CAMU_SetTrimming")]
fn set_trimming(&mut self, trimming: Trimming);
/// Returns whether or not trimming is currently enabled for the camera. /// Returns whether or not trimming is currently enabled for the camera.
#[doc(alias = "CAMU_IsTrimming")] #[doc(alias = "CAMU_IsTrimming")]
fn is_trimming_enabled(&self) -> crate::Result<bool> { fn is_trimming_enabled(&self) -> crate::Result<bool> {
@ -420,76 +626,32 @@ pub trait Camera {
} }
} }
/// Set trimming bounds based on image coordinates. /// Returns the [`Trimming`] configuration currently set.
/// ///
/// For trimming to take effect it is required to pass `true` into [`Camera::set_trimming()`]. /// # Notes
#[doc(alias = "CAMU_SetTrimmingParams")] ///
fn set_trimming_params(&mut self, params: TrimmingParams) -> crate::Result<()> { /// If successful, this function will always return a [`Trimming::Absolute`].
unsafe {
ResultCode(ctru_sys::CAMU_SetTrimmingParams(
self.port_as_raw(),
params.x_start,
params.y_start,
params.x_end,
params.y_end,
))?;
Ok(())
}
}
/// Returns the [`TrimmingParams`] currently set.
#[doc(alias = "CAMU_GetTrimmingParams")] #[doc(alias = "CAMU_GetTrimmingParams")]
fn trimming_params(&self) -> crate::Result<TrimmingParams> { fn trimming_configuration(&self) -> crate::Result<Trimming> {
unsafe { unsafe {
let mut x_start = 0; let mut top_left = (0, 0);
let mut y_start = 0; let mut bottom_right = (0, 0);
let mut x_end = 0;
let mut y_end = 0;
ResultCode(ctru_sys::CAMU_GetTrimmingParams( ResultCode(ctru_sys::CAMU_GetTrimmingParams(
&mut x_start, &mut top_left.0,
&mut y_start, &mut top_left.1,
&mut x_end, &mut bottom_right.0,
&mut y_end, &mut bottom_right.1,
self.port_as_raw(), self.port_as_raw(),
))?; ))?;
Ok(TrimmingParams { Ok(Trimming::Absolute {
x_start, top_left,
y_start, bottom_right,
x_end,
y_end,
}) })
} }
} }
/// Set the trimming bounds relatively to the center of the image. /// Set the exposure level of the camera.
///
/// # Notes
///
/// The new width will be `trim_width / 2` to the left and right of the center.
/// The new height will be `trim_height / 2` above and below the center.
// TODO: This function doesn't use `TrimmingParams`. It'd be better to merge it with `set_trimming_params()` and change the `TrimmingParams` representation.
#[doc(alias = "CAMU_SetTrimmingParamsCenter")]
fn set_trimming_params_center(
&mut self,
trim_width: i16,
trim_height: i16,
cam_width: i16,
cam_height: i16,
) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter(
self.port_as_raw(),
trim_width,
trim_height,
cam_width,
cam_height,
))?;
Ok(())
}
}
/// Set the exposure level of the camera.å
#[doc(alias = "CAMU_SetExposure")] #[doc(alias = "CAMU_SetExposure")]
fn set_exposure(&mut self, exposure: i8) -> crate::Result<()> { fn set_exposure(&mut self, exposure: i8) -> crate::Result<()> {
unsafe { unsafe {
@ -560,31 +722,6 @@ pub trait Camera {
} }
} }
/// Set whether auto white balance is enabled or disabled for the camera.
#[doc(alias = "CAMU_SetAutoWhiteBalance")]
fn set_auto_white_balance(&mut self, enabled: bool) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetAutoWhiteBalance(
self.camera_as_raw(),
enabled,
))?;
Ok(())
}
}
/// Returns `true` if auto white balance is enabled for the camera.
#[doc(alias = "CAMU_IsAutoWhiteBalance")]
fn is_auto_white_balance_enabled(&self) -> crate::Result<bool> {
unsafe {
let mut enabled = false;
ResultCode(ctru_sys::CAMU_IsAutoWhiteBalance(
&mut enabled,
self.camera_as_raw(),
))?;
Ok(enabled)
}
}
/// Set the flip mode of the camera's image. /// Set the flip mode of the camera's image.
#[doc(alias = "CAMU_FlipImage")] #[doc(alias = "CAMU_FlipImage")]
fn flip_image(&mut self, flip: FlipMode) -> crate::Result<()> { fn flip_image(&mut self, flip: FlipMode) -> crate::Result<()> {
@ -598,54 +735,13 @@ pub trait Camera {
} }
} }
/// Set the image resolution of the camera in detail. /// Set the view size of the camera.
///
/// # Errors
///
/// This function will error if the coordinates of the first crop point are greater than the
/// coordinates of the second crop point.
/// ///
/// # Arguments /// # Notes
/// ///
/// * `width` - Width of the image /// Calling this function will reset the trimming configuration.
/// * `height` - height of the image
/// * `crop_0` - The first crop point in which the image will be trimmed
/// * `crop_1` - The second crop point in which the image will be trimmed
#[doc(alias = "CAMU_SetDetailSize")]
fn set_detail_size(
&mut self,
width: i16,
height: i16,
crop_0: (i16, i16),
crop_1: (i16, i16),
) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetDetailSize(
self.camera_as_raw(),
width,
height,
crop_0.0,
crop_0.1,
crop_1.0,
crop_1.1,
ctru_sys::CONTEXT_A,
))?;
Ok(())
}
}
/// Set the view size of the camera.
#[doc(alias = "CAMU_SetSize")] #[doc(alias = "CAMU_SetSize")]
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> { fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()>;
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
Ok(())
}
}
/// Set the frame rate of the camera. /// Set the frame rate of the camera.
#[doc(alias = "CAMU_SetFrameRate")] #[doc(alias = "CAMU_SetFrameRate")]
@ -867,59 +963,119 @@ pub trait Camera {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
// TODO: This should use the value passed within `set_view_size` rather than arbitrary `width` and `height` values. fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> {
// Furthermore, it's pretty unclear what the "default" view size is. What happens if the user doesn't set it before taking the picture? let full_view: (i16, i16) = self.view_size().into();
fn take_picture(
&mut self,
buffer: &mut [u8],
width: u16,
height: u16,
timeout: Duration,
) -> crate::Result<()> {
let transfer_unit = unsafe {
let mut buf_size = 0;
ResultCode(ctru_sys::CAMU_GetMaxBytes(
&mut buf_size,
width as i16,
height as i16,
))?;
Ok::<u32, i32>(buf_size)
}?;
unsafe {
ResultCode(ctru_sys::CAMU_SetTransferBytes(
self.port_as_raw(),
transfer_unit,
width as i16,
height as i16,
))?;
};
let screen_size: usize = usize::from(width) * usize::from(height) * 2; // Check whether the input buffer is big enough for the image.
if buffer.len() < screen_size { let max_size =
(final_view.0 as usize * final_view.1 as usize) * std::mem::size_of::<i16>();
if buffer.len() < max_size {
return Err(Error::BufferTooShort { return Err(Error::BufferTooShort {
provided: buffer.len(), provided: buffer.len(),
wanted: screen_size, wanted: max_size,
}); });
} }
unsafe { unsafe {
ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?;
ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?; };
ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?;
let transfer_unit = match self.trimming() {
Trimming::Absolute {
top_left,
bottom_right,
} => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParams(
self.port_as_raw(),
top_left.0,
top_left.1,
bottom_right.0,
bottom_right.1,
))?;
// The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is...
let transfer_unit = unsafe {
let mut transfer_unit = 0;
ResultCode(ctru_sys::CAMU_GetMaxBytes(
&mut transfer_unit,
final_view.0,
final_view.1,
))?;
transfer_unit
};
unsafe {
ResultCode(ctru_sys::CAMU_SetTransferBytes(
self.port_as_raw(),
transfer_unit,
final_view.0,
final_view.1,
))?;
};
transfer_unit
},
Trimming::Centered { width, height } => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter(
self.port_as_raw(),
width,
height,
full_view.0,
full_view.1,
))?;
// The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is...
let transfer_unit = unsafe {
let mut transfer_unit = 0;
ResultCode(ctru_sys::CAMU_GetMaxBytes(
&mut transfer_unit,
width,
height,
))?;
transfer_unit
};
unsafe {
ResultCode(ctru_sys::CAMU_SetTransferBytes(
self.port_as_raw(),
transfer_unit,
width,
height,
))?;
};
transfer_unit
},
Trimming::Off => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
},
}; };
let receive_event = unsafe { let receive_event = unsafe {
let mut completion_handle: Handle = 0; let mut completion_handle: Handle = 0;
ResultCode(ctru_sys::CAMU_SetReceiving( ResultCode(ctru_sys::CAMU_SetReceiving(
&mut completion_handle, &mut completion_handle,
buffer.as_mut_ptr().cast(), buffer.as_mut_ptr().cast(),
self.port_as_raw(), self.port_as_raw(),
screen_size as u32, max_size as u32,
transfer_unit.try_into().unwrap(), transfer_unit.try_into().unwrap(),
))?; ))?;
Ok::<Handle, i32>(completion_handle)
}?; completion_handle
};
unsafe {
ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?;
};
unsafe { unsafe {
// Panicking without closing an SVC handle causes an ARM exception, we have to handle it carefully (TODO: SVC module) // Panicking without closing an SVC handle causes an ARM exception, we have to handle it carefully (TODO: SVC module)
@ -930,7 +1086,10 @@ pub trait Camera {
// We close everything first, then we check for possible errors // We close everything first, then we check for possible errors
let _ = ctru_sys::svcCloseHandle(receive_event); // We wouldn't return the error even if there was one, so no use of ResultCode is needed let _ = ctru_sys::svcCloseHandle(receive_event); // We wouldn't return the error even if there was one, so no use of ResultCode is needed
// Camera state cleanup
ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?; ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?;
ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))?; ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))?;
wait_result?; wait_result?;
@ -943,6 +1102,11 @@ pub trait Camera {
impl Cam { impl Cam {
/// Initialize a new service handle. /// Initialize a new service handle.
/// ///
/// # Notes
///
/// All cameras default to taking photos with [`ViewSize::TopLCD`] and [`OutputFormat::Yuv422`].
/// Have a look at [`Camera::set_view_size()`] and [`Camera::set_output_format()`] to change these settings.
///
/// # Errors /// # Errors
/// ///
/// This function will return an error if the service was unable to be initialized. /// This function will return an error if the service was unable to be initialized.
@ -964,15 +1128,43 @@ impl Cam {
/// ``` /// ```
#[doc(alias = "camInit")] #[doc(alias = "camInit")]
pub fn new() -> crate::Result<Cam> { pub fn new() -> crate::Result<Cam> {
unsafe { let _service_handler = ServiceReference::new(
ResultCode(ctru_sys::camInit())?; &CAM_ACTIVE,
Ok(Cam { || {
inner_cam: InwardCam, ResultCode(unsafe { ctru_sys::camInit() })?;
outer_right_cam: OutwardRightCam,
outer_left_cam: OutwardLeftCam, Ok(())
both_outer_cams: BothOutwardCam, },
}) || unsafe {
} ctru_sys::camExit();
},
)?;
let mut inner_cam = InwardCam {
view_size: ViewSize::TopLCD,
};
let mut outer_right_cam = OutwardRightCam {
view_size: ViewSize::TopLCD,
};
let mut outer_left_cam = OutwardLeftCam {
view_size: ViewSize::TopLCD,
};
let mut both_outer_cams = BothOutwardCam {
view_size: ViewSize::TopLCD,
};
inner_cam.set_view_size(ViewSize::TopLCD)?;
outer_right_cam.set_view_size(ViewSize::TopLCD)?;
outer_left_cam.set_view_size(ViewSize::TopLCD)?;
both_outer_cams.set_view_size(ViewSize::TopLCD)?;
Ok(Cam {
_service_handler,
inner_cam,
outer_right_cam,
outer_left_cam,
both_outer_cams,
})
} }
/// Play the specified sound based on the [`ShutterSound`] argument /// Play the specified sound based on the [`ShutterSound`] argument
@ -1007,13 +1199,6 @@ impl Cam {
} }
} }
impl Drop for Cam {
#[doc(alias = "camExit")]
fn drop(&mut self) {
unsafe { ctru_sys::camExit() };
}
}
impl TryFrom<FramebufferFormat> for OutputFormat { impl TryFrom<FramebufferFormat> for OutputFormat {
type Error = (); type Error = ();
@ -1036,6 +1221,21 @@ impl TryFrom<OutputFormat> for FramebufferFormat {
} }
} }
impl From<ViewSize> for (i16, i16) {
fn from(value: ViewSize) -> Self {
match value {
ViewSize::TopLCD => (400, 240),
ViewSize::BottomLCD => (320, 240),
ViewSize::Vga => (640, 480),
ViewSize::QQVga => (160, 120),
ViewSize::Cif => (352, 288),
ViewSize::QCif => (176, 144),
ViewSize::DS => (256, 192),
ViewSize::DSX4 => (512, 384),
}
}
}
from_impl!(FlipMode, ctru_sys::CAMU_Flip); from_impl!(FlipMode, ctru_sys::CAMU_Flip);
from_impl!(ViewSize, ctru_sys::CAMU_Size); from_impl!(ViewSize, ctru_sys::CAMU_Size);
from_impl!(FrameRate, ctru_sys::CAMU_FrameRate); from_impl!(FrameRate, ctru_sys::CAMU_FrameRate);

29
ctru-rs/src/services/hid.rs

@ -1,7 +1,7 @@
//! Human Interface Device service. //! Human Interface Device service.
//! //!
//! The HID service provides read access to user input such as [button presses](Hid::keys_down), [touch screen presses](Hid::touch_position), //! The HID service provides read access to user input such as [button presses](Hid::keys_down), [touch screen presses](Hid::touch_position),
//! and [circle pad information](Hid::circlepad_position). It also provides information from the [3D slider](Hid::slider_3d()), the [volume slider](Hid::slider_volume()), //! and [circle pad information](Hid::circlepad_position). It also provides information from the [volume slider](Hid::slider_volume()),
//! the [accelerometer](Hid::accelerometer_vector()), and the [gyroscope](Hid::gyroscope_rate()). //! the [accelerometer](Hid::accelerometer_vector()), and the [gyroscope](Hid::gyroscope_rate()).
#![doc(alias = "input")] #![doc(alias = "input")]
#![doc(alias = "controller")] #![doc(alias = "controller")]
@ -344,7 +344,7 @@ impl Hid {
/// # } /// # }
/// ``` /// ```
#[doc(alias = "HIDUSER_GetSoundVolume")] #[doc(alias = "HIDUSER_GetSoundVolume")]
pub fn slider_volume(&self) -> f32 { pub fn volume_slider(&self) -> f32 {
let mut slider = 0; let mut slider = 0;
unsafe { unsafe {
@ -354,29 +354,6 @@ impl Hid {
(slider as f32) / 63. (slider as f32) / 63.
} }
/// Returns the current 3D slider position (between 0 and 1).
///
/// # Example
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::hid::Hid;
/// let mut hid = Hid::new()?;
///
/// hid.scan_input();
///
/// let volume = hid.slider_3d();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "osGet3DSliderState")]
pub fn slider_3d(&self) -> f32 {
unsafe { ctru_sys::osGet3DSliderState() }
}
/// Activate/deactivate the console's acceleration sensor. /// Activate/deactivate the console's acceleration sensor.
/// ///
/// # Example /// # Example
@ -478,7 +455,7 @@ impl Hid {
Ok((res.x, res.y, res.z)) Ok((res.x, res.y, res.z))
} }
/// Returns the angular rate (x,y,z) registered by the gyroscope. /// Returns the angular rate (roll,pitch,yaw) registered by the gyroscope.
/// ///
/// # Errors /// # Errors
/// ///

Loading…
Cancel
Save