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() { @@ -25,12 +25,11 @@ fn main() {
// Get information about which keys were held down on this frame.
let keys = hid.keys_held();
// Print the status of the 2 sliders.
// Print the status of the volume slider.
println!(
"\x1b[20;0HVolume slider: {} ",
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
// from what they were on the previous frame.

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

@ -3,17 +3,16 @@ @@ -3,17 +3,16 @@
//! 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::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::gspgpu::FramebufferFormat;
use std::time::Duration;
const WIDTH: usize = 400;
const HEIGHT: usize = 240;
// 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 WIDTH: usize = 200;
const HEIGHT: usize = 100;
const WAIT_TIMEOUT: Duration = Duration::from_millis(300);
@ -35,30 +34,35 @@ fn main() { @@ -35,30 +34,35 @@ fn main() {
let mut cam = Cam::new().expect("Failed to initialize CAM service.");
// Camera setup.
{
let camera = &mut cam.outer_right_cam;
camera
.set_view_size(ViewSize::TopLCD)
.expect("Failed to set camera size");
camera
.set_output_format(OutputFormat::Rgb565)
.expect("Failed to set camera output format");
camera
.set_noise_filter(true)
.expect("Failed to enable noise filter");
camera
.set_auto_exposure(true)
.expect("Failed to enable auto exposure");
camera
.set_auto_white_balance(true)
.expect("Failed to enable auto white balance");
camera
.set_trimming(false)
.expect("Failed to disable trimming");
}
let mut buf = vec![0u8; BUF_SIZE];
let camera = &mut cam.outer_right_cam;
camera
.set_view_size(ViewSize::TopLCD)
.expect("Failed to set camera size");
camera
.set_output_format(OutputFormat::Rgb565)
.expect("Failed to set camera output format");
camera
.set_noise_filter(true)
.expect("Failed to enable noise filter");
camera
.set_auto_exposure(true)
.expect("Failed to enable auto exposure");
camera
.set_white_balance(WhiteBalance::Auto)
.expect("Failed to enable auto white balance");
camera
.set_trimming(Trimming::Centered {
width: WIDTH as i16,
height: HEIGHT as i16,
})
.expect("Failed to disable trimming");
// 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!("Press Start to exit");
@ -79,20 +83,24 @@ fn main() { @@ -79,20 +83,24 @@ fn main() {
// Take a picture and write it to the buffer.
camera
.take_picture(
&mut buf,
WIDTH.try_into().unwrap(),
HEIGHT.try_into().unwrap(),
WAIT_TIMEOUT,
)
.take_picture(&mut buf, WAIT_TIMEOUT)
.expect("Failed to take picture");
let image_size = camera
.final_image_size()
.expect("could not retrieve final image size");
// Play the normal shutter sound.
cam.play_shutter_sound(ShutterSound::Normal)
.expect("Failed to play shutter sound");
// 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`.
top_screen.flush_buffers();

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

@ -6,12 +6,17 @@ @@ -6,12 +6,17 @@
use crate::error::{Error, ResultCode};
use crate::services::gspgpu::FramebufferFormat;
use crate::services::ServiceReference;
use ctru_sys::Handle;
use std::sync::Mutex;
use std::time::Duration;
static CAM_ACTIVE: Mutex<()> = Mutex::new(());
/// Handle to the Camera service.
#[non_exhaustive]
pub struct Cam {
_service_handler: ServiceReference,
/// Inside-facing camera.
pub inner_cam: InwardCam,
/// Outside-facing right camera.
@ -224,33 +229,25 @@ pub enum ShutterSound { @@ -224,33 +229,25 @@ pub enum ShutterSound {
MovieEnd = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE_END,
}
/// Parameters to handle image trimming.
///
/// See [`Camera::set_trimming_params()`] to learn how to use this.
/// Configuration to handle image trimming.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TrimmingParams {
x_start: i16,
y_start: i16,
x_end: i16,
y_end: i16,
}
impl TrimmingParams {
/// Creates a new [`TrimmingParams`] and guarantees the start coordinates are less than or
/// equal to the end coordinates.
///
/// # Panics
///
/// This function panics if the start coordinates are larger than the end coordinates (for each axis).
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);
Self {
x_start,
y_start,
x_end,
y_end,
}
}
pub enum Trimming {
/// Trimming configuration based on absolute coordinates of the image.
Absolute {
/// Top-left corner coordinates (x,y) of the trimmed area.
top_left: (i16, i16),
/// Bottom-right corner coordinates (x,y) of the trimmed area.
bottom_right: (i16, i16),
},
/// Trimming configuration relatively to the center of the image.
Centered {
/// Width of the trimmed area.
width: i16,
/// Height of the trimmed area.
height: i16,
},
/// Trimming disabled.
Off,
}
/// 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 @@ -268,39 +265,33 @@ pub struct StereoCameraCalibrationData(pub ctru_sys::CAMU_StereoCameraCalibratio
///
/// Usually used for selfies.
#[non_exhaustive]
pub struct InwardCam;
impl Camera for InwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1
}
pub struct InwardCam {
view_size: ViewSize,
trimming: Trimming,
}
/// Right-side outward camera representation.
#[non_exhaustive]
pub struct OutwardRightCam;
impl Camera for OutwardRightCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1
}
pub struct OutwardRightCam {
view_size: ViewSize,
trimming: Trimming,
}
/// Left-side outward camera representation.
#[non_exhaustive]
pub struct OutwardLeftCam;
impl Camera for OutwardLeftCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT2
}
pub struct OutwardLeftCam {
view_size: ViewSize,
trimming: Trimming,
}
/// Both outer cameras combined.
///
/// Usually used for 3D photos.
#[non_exhaustive]
pub struct BothOutwardCam;
pub struct BothOutwardCam {
view_size: ViewSize,
trimming: Trimming,
}
impl BothOutwardCam {
/// Set whether to enable or disable brightness synchronization between the two cameras.
@ -318,6 +309,148 @@ impl BothOutwardCam { @@ -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 {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1_OUT2
@ -326,14 +459,41 @@ impl Camera for BothOutwardCam { @@ -326,14 +459,41 @@ impl Camera for BothOutwardCam {
fn port_as_raw(&self) -> ctru_sys::u32_ {
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.
// 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 {
/// Returns the raw value of the selected camera.
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.
fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_CAM1
@ -367,7 +527,7 @@ pub trait Camera { @@ -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.
///
/// # Example
@ -381,35 +541,81 @@ pub trait Camera { @@ -381,35 +541,81 @@ pub trait Camera {
///
/// let inward = &cam.inner_cam;
///
/// // Inward cam is not busy since it is not being used.
/// let transfer_count = inward.transfer_byte_count();
/// let transfer_count = inward.max_byte_count();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "CAMU_GetTransferBytes")]
fn transfer_byte_count(&self) -> crate::Result<u32> {
unsafe {
let mut transfer_bytes = 0;
ResultCode(ctru_sys::CAMU_GetTransferBytes(
&mut transfer_bytes,
self.port_as_raw(),
))?;
Ok(transfer_bytes)
fn max_byte_count(&self) -> crate::Result<usize> {
let size = self.final_image_size()?;
let mut res: usize = (size.0 as usize * size.1 as usize) * std::mem::size_of::<i16>();
// 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 {
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`].
#[doc(alias = "CAMU_SetTrimming")]
fn set_trimming(&mut self, enabled: bool) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), enabled))?;
Ok(())
/// # Example
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// 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.
#[doc(alias = "CAMU_IsTrimming")]
fn is_trimming_enabled(&self) -> crate::Result<bool> {
@ -420,76 +626,32 @@ pub trait Camera { @@ -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()`].
#[doc(alias = "CAMU_SetTrimmingParams")]
fn set_trimming_params(&mut self, params: TrimmingParams) -> crate::Result<()> {
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.
/// # Notes
///
/// If successful, this function will always return a [`Trimming::Absolute`].
#[doc(alias = "CAMU_GetTrimmingParams")]
fn trimming_params(&self) -> crate::Result<TrimmingParams> {
fn trimming_configuration(&self) -> crate::Result<Trimming> {
unsafe {
let mut x_start = 0;
let mut y_start = 0;
let mut x_end = 0;
let mut y_end = 0;
let mut top_left = (0, 0);
let mut bottom_right = (0, 0);
ResultCode(ctru_sys::CAMU_GetTrimmingParams(
&mut x_start,
&mut y_start,
&mut x_end,
&mut y_end,
&mut top_left.0,
&mut top_left.1,
&mut bottom_right.0,
&mut bottom_right.1,
self.port_as_raw(),
))?;
Ok(TrimmingParams {
x_start,
y_start,
x_end,
y_end,
Ok(Trimming::Absolute {
top_left,
bottom_right,
})
}
}
/// Set the trimming bounds relatively to the center of the image.
///
/// # 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.å
/// Set the exposure level of the camera.
#[doc(alias = "CAMU_SetExposure")]
fn set_exposure(&mut self, exposure: i8) -> crate::Result<()> {
unsafe {
@ -560,31 +722,6 @@ pub trait Camera { @@ -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.
#[doc(alias = "CAMU_FlipImage")]
fn flip_image(&mut self, flip: FlipMode) -> crate::Result<()> {
@ -598,54 +735,13 @@ pub trait Camera { @@ -598,54 +735,13 @@ pub trait Camera {
}
}
/// Set the image resolution of the camera in detail.
///
/// # Errors
///
/// This function will error if the coordinates of the first crop point are greater than the
/// coordinates of the second crop point.
/// Set the view size of the camera.
///
/// # Arguments
/// # Notes
///
/// * `width` - Width of the image
/// * `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.
/// Calling this function will reset the trimming configuration.
#[doc(alias = "CAMU_SetSize")]
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(())
}
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()>;
/// Set the frame rate of the camera.
#[doc(alias = "CAMU_SetFrameRate")]
@ -867,59 +963,119 @@ pub trait Camera { @@ -867,59 +963,119 @@ pub trait Camera {
/// # Ok(())
/// # }
/// ```
// TODO: This should use the value passed within `set_view_size` rather than arbitrary `width` and `height` values.
// Furthermore, it's pretty unclear what the "default" view size is. What happens if the user doesn't set it before taking the picture?
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,
))?;
};
fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> {
let full_view: (i16, i16) = self.view_size().into();
let screen_size: usize = usize::from(width) * usize::from(height) * 2;
if buffer.len() < screen_size {
// Check whether the input buffer is big enough for the image.
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 {
provided: buffer.len(),
wanted: screen_size,
wanted: max_size,
});
}
unsafe {
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 mut completion_handle: Handle = 0;
ResultCode(ctru_sys::CAMU_SetReceiving(
&mut completion_handle,
buffer.as_mut_ptr().cast(),
self.port_as_raw(),
screen_size as u32,
max_size as u32,
transfer_unit.try_into().unwrap(),
))?;
Ok::<Handle, i32>(completion_handle)
}?;
completion_handle
};
unsafe {
ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?;
};
unsafe {
// 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 { @@ -930,7 +1086,10 @@ pub trait Camera {
// 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
// Camera state cleanup
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))?;
wait_result?;
@ -943,6 +1102,11 @@ pub trait Camera { @@ -943,6 +1102,11 @@ pub trait Camera {
impl Cam {
/// 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
///
/// This function will return an error if the service was unable to be initialized.
@ -964,15 +1128,43 @@ impl Cam { @@ -964,15 +1128,43 @@ impl Cam {
/// ```
#[doc(alias = "camInit")]
pub fn new() -> crate::Result<Cam> {
unsafe {
ResultCode(ctru_sys::camInit())?;
Ok(Cam {
inner_cam: InwardCam,
outer_right_cam: OutwardRightCam,
outer_left_cam: OutwardLeftCam,
both_outer_cams: BothOutwardCam,
})
}
let _service_handler = ServiceReference::new(
&CAM_ACTIVE,
|| {
ResultCode(unsafe { ctru_sys::camInit() })?;
Ok(())
},
|| 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
@ -1007,13 +1199,6 @@ impl Cam { @@ -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 {
type Error = ();
@ -1036,6 +1221,21 @@ impl TryFrom<OutputFormat> for FramebufferFormat { @@ -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!(ViewSize, ctru_sys::CAMU_Size);
from_impl!(FrameRate, ctru_sys::CAMU_FrameRate);

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

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
//! 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),
//! 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()).
#![doc(alias = "input")]
#![doc(alias = "controller")]
@ -344,7 +344,7 @@ impl Hid { @@ -344,7 +344,7 @@ impl Hid {
/// # }
/// ```
#[doc(alias = "HIDUSER_GetSoundVolume")]
pub fn slider_volume(&self) -> f32 {
pub fn volume_slider(&self) -> f32 {
let mut slider = 0;
unsafe {
@ -354,29 +354,6 @@ impl Hid { @@ -354,29 +354,6 @@ impl Hid {
(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.
///
/// # Example
@ -478,7 +455,7 @@ impl Hid { @@ -478,7 +455,7 @@ impl Hid {
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
///

Loading…
Cancel
Save