1#![doc(alias = "camera")]
6
7use crate::error::{Error, ResultCode};
8use crate::services::ServiceReference;
9use crate::services::gspgpu::FramebufferFormat;
10use ctru_sys::Handle;
11use private::Configuration;
12
13use std::sync::Mutex;
14use std::time::Duration;
15
16static CAM_ACTIVE: Mutex<()> = Mutex::new(());
17
18pub struct Cam {
20 _service_handler: ServiceReference,
21 pub inner_cam: InwardCam,
23 pub outer_right_cam: OutwardRightCam,
25 pub outer_left_cam: OutwardLeftCam,
27 pub both_outer_cams: BothOutwardCam,
29}
30
31#[doc(alias = "CAMU_Flip")]
35#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36#[repr(u8)]
37pub enum FlipMode {
38 None = ctru_sys::FLIP_NONE,
40 Horizontal = ctru_sys::FLIP_HORIZONTAL,
42 Vertical = ctru_sys::FLIP_VERTICAL,
44 Reverse = ctru_sys::FLIP_REVERSE,
46}
47
48#[doc(alias = "CAMU_Size")]
52#[derive(Copy, Clone, Debug, PartialEq, Eq)]
53#[repr(u8)]
54pub enum ViewSize {
55 TopLCD = ctru_sys::SIZE_CTR_TOP_LCD,
59 BottomLCD = ctru_sys::SIZE_CTR_BOTTOM_LCD,
63 Vga = ctru_sys::SIZE_VGA,
65 QQVga = ctru_sys::SIZE_QQVGA,
67 Cif = ctru_sys::SIZE_CIF,
69 QCif = ctru_sys::SIZE_QCIF,
71 DS = ctru_sys::SIZE_DS_LCD,
73 DSX4 = ctru_sys::SIZE_DS_LCDx4,
75}
76
77#[doc(alias = "CAMU_FramRate")]
81#[derive(Copy, Clone, Debug, PartialEq, Eq)]
82#[repr(u8)]
83pub enum FrameRate {
84 Fps15 = ctru_sys::FRAME_RATE_15,
86 Fps15To5 = ctru_sys::FRAME_RATE_15_TO_5,
88 Fps15To2 = ctru_sys::FRAME_RATE_15_TO_2,
90 Fps10 = ctru_sys::FRAME_RATE_10,
92 Fps8_5 = ctru_sys::FRAME_RATE_8_5,
94 Fps5 = ctru_sys::FRAME_RATE_5,
96 Fps20 = ctru_sys::FRAME_RATE_20,
98 Fps20To5 = ctru_sys::FRAME_RATE_20_TO_5,
100 Fps30 = ctru_sys::FRAME_RATE_30,
102 Fps30To5 = ctru_sys::FRAME_RATE_30_TO_5,
104 Fps15To10 = ctru_sys::FRAME_RATE_15_TO_10,
106 Fps20To10 = ctru_sys::FRAME_RATE_20_TO_10,
108 Fps30To10 = ctru_sys::FRAME_RATE_30_TO_10,
110}
111
112#[doc(alias = "CAMU_WhiteBalance")]
116#[derive(Copy, Clone, Debug, PartialEq, Eq)]
117#[repr(u8)]
118pub enum WhiteBalance {
119 Auto = ctru_sys::WHITE_BALANCE_AUTO,
121 Temp3200K = ctru_sys::WHITE_BALANCE_3200K,
123 Temp4150K = ctru_sys::WHITE_BALANCE_4150K,
125 Temp5200K = ctru_sys::WHITE_BALANCE_5200K,
127 Temp6000K = ctru_sys::WHITE_BALANCE_6000K,
129 Temp7000K = ctru_sys::WHITE_BALANCE_7000K,
131}
132
133#[doc(alias = "CAMU_PhotoMode")]
137#[derive(Copy, Clone, Debug, PartialEq, Eq)]
138#[repr(u8)]
139pub enum PhotoMode {
140 Normal = ctru_sys::PHOTO_MODE_NORMAL,
142 Portrait = ctru_sys::PHOTO_MODE_PORTRAIT,
144 Landscape = ctru_sys::PHOTO_MODE_LANDSCAPE,
146 NightView = ctru_sys::PHOTO_MODE_NIGHTVIEW,
148 Letter = ctru_sys::PHOTO_MODE_LETTER,
150}
151
152#[doc(alias = "CAMU_Effect")]
156#[derive(Copy, Clone, Debug, PartialEq, Eq)]
157#[repr(u8)]
158pub enum Effect {
159 None = ctru_sys::EFFECT_NONE,
161 Mono = ctru_sys::EFFECT_MONO,
163 Sepia = ctru_sys::EFFECT_SEPIA,
165 Negative = ctru_sys::EFFECT_NEGATIVE,
167 Negafilm = ctru_sys::EFFECT_NEGAFILM,
169 Sepia01 = ctru_sys::EFFECT_SEPIA01,
173}
174
175#[doc(alias = "CAMU_Contrast")]
179#[derive(Copy, Clone, Debug, PartialEq, Eq)]
180#[repr(u8)]
181pub enum Contrast {
182 Low = ctru_sys::CONTRAST_LOW,
184 Normal = ctru_sys::CONTRAST_NORMAL,
186 High = ctru_sys::CONTRAST_HIGH,
188}
189
190#[doc(alias = "CAMU_LensCorrection")]
194#[derive(Copy, Clone, Debug, PartialEq, Eq)]
195#[repr(u8)]
196pub enum LensCorrection {
197 Off = ctru_sys::LENS_CORRECTION_DARK,
199 Normal = ctru_sys::LENS_CORRECTION_NORMAL,
201 Bright = ctru_sys::LENS_CORRECTION_BRIGHT,
203}
204
205#[doc(alias = "CAMU_OutputFormat")]
209#[derive(Copy, Clone, Debug, PartialEq, Eq)]
210#[repr(u8)]
211pub enum OutputFormat {
212 Yuv422 = ctru_sys::OUTPUT_YUV_422,
214 Rgb565 = ctru_sys::OUTPUT_RGB_565,
216}
217
218#[doc(alias = "CAMU_ShutterSoundType")]
222#[derive(Copy, Clone, Debug, PartialEq, Eq)]
223#[repr(u8)]
224pub enum ShutterSound {
225 Normal = ctru_sys::SHUTTER_SOUND_TYPE_NORMAL,
227 Movie = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE,
229 MovieEnd = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE_END,
231}
232
233#[non_exhaustive]
238#[derive(Clone, Copy, Debug, PartialEq, Eq)]
239pub enum Trimming {
240 #[allow(missing_docs)]
242 Centered { width: i16, height: i16 },
243 Off,
245}
246
247#[doc(alias = "CAMU_ImageQualityCalibrationData")]
250#[derive(Default, Clone, Copy, Debug)]
251pub struct ImageQualityCalibration(pub ctru_sys::CAMU_ImageQualityCalibrationData);
252
253#[doc(alias = "CAMU_StereoCameraCalibrationData")]
256#[derive(Default, Clone, Copy, Debug)]
257pub struct StereoCameraCalibration(ctru_sys::CAMU_StereoCameraCalibrationData);
258
259pub struct InwardCam {
263 configuration: Configuration,
264}
265
266pub struct OutwardRightCam {
268 configuration: Configuration,
269}
270
271pub struct OutwardLeftCam {
273 configuration: Configuration,
274}
275
276pub struct BothOutwardCam {
280 configuration: Configuration,
281}
282
283mod private {
284 use super::{BothOutwardCam, InwardCam, OutwardLeftCam, OutwardRightCam, Trimming, ViewSize};
285
286 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
288 pub struct Configuration {
289 pub view_size: ViewSize,
290 pub trimming: Trimming,
291 }
292
293 impl Configuration {
294 pub fn new() -> Self {
295 Self::default()
296 }
297 }
298
299 impl Default for Configuration {
300 fn default() -> Self {
301 Self {
302 view_size: ViewSize::TopLCD,
303 trimming: Trimming::Off,
304 }
305 }
306 }
307
308 pub trait ConfigurableCamera {
309 fn configuration(&self) -> &Configuration;
310
311 fn configuration_mut(&mut self) -> &mut Configuration;
312 }
313
314 impl ConfigurableCamera for InwardCam {
315 fn configuration(&self) -> &Configuration {
316 &self.configuration
317 }
318
319 fn configuration_mut(&mut self) -> &mut Configuration {
320 &mut self.configuration
321 }
322 }
323
324 impl ConfigurableCamera for OutwardRightCam {
325 fn configuration(&self) -> &Configuration {
326 &self.configuration
327 }
328
329 fn configuration_mut(&mut self) -> &mut Configuration {
330 &mut self.configuration
331 }
332 }
333
334 impl ConfigurableCamera for OutwardLeftCam {
335 fn configuration(&self) -> &Configuration {
336 &self.configuration
337 }
338
339 fn configuration_mut(&mut self) -> &mut Configuration {
340 &mut self.configuration
341 }
342 }
343
344 impl ConfigurableCamera for BothOutwardCam {
345 fn configuration(&self) -> &Configuration {
346 &self.configuration
347 }
348
349 fn configuration_mut(&mut self) -> &mut Configuration {
350 &mut self.configuration
351 }
352 }
353}
354
355impl BothOutwardCam {
356 #[doc(alias = "CAMU_SetBrightnessSynchronization")]
358 pub fn set_brightness_synchronization(
359 &mut self,
360 brightness_synchronization: bool,
361 ) -> crate::Result<()> {
362 unsafe {
363 ResultCode(ctru_sys::CAMU_SetBrightnessSynchronization(
364 brightness_synchronization,
365 ))?;
366 }
367
368 Ok(())
369 }
370
371 #[doc(alias = "CAMU_GetStereoCameraCalibrationData")]
372 pub fn stereo_calibration(&self) -> crate::Result<StereoCameraCalibration> {
374 let mut calibration = StereoCameraCalibration::default();
375
376 unsafe {
377 ResultCode(ctru_sys::CAMU_GetStereoCameraCalibrationData(
378 &mut calibration.0,
379 ))?;
380 }
381
382 Ok(calibration)
383 }
384
385 #[doc(alias = "CAMU_SetStereoCameraCalibrationData")]
386 pub fn set_stereo_calibration(
389 &mut self,
390 mut stereo_calibration: StereoCameraCalibration,
391 ) -> crate::Result<()> {
392 let view_size = self.final_view_size();
393
394 stereo_calibration.0.imageWidth = view_size.0;
395 stereo_calibration.0.imageHeight = view_size.1;
396
397 unsafe {
398 ResultCode(ctru_sys::CAMU_SetStereoCameraCalibrationData(
399 stereo_calibration.0,
400 ))?;
401 }
402
403 Ok(())
404 }
405}
406
407impl Camera for InwardCam {
408 fn camera_as_raw(&self) -> ctru_sys::u32_ {
409 ctru_sys::SELECT_IN1.into()
410 }
411}
412
413impl Camera for OutwardRightCam {
414 fn camera_as_raw(&self) -> ctru_sys::u32_ {
415 ctru_sys::SELECT_OUT1.into()
416 }
417}
418
419impl Camera for OutwardLeftCam {
420 fn camera_as_raw(&self) -> ctru_sys::u32_ {
421 ctru_sys::SELECT_OUT2.into()
422 }
423}
424
425impl Camera for BothOutwardCam {
426 fn camera_as_raw(&self) -> ctru_sys::u32_ {
427 ctru_sys::SELECT_OUT1_OUT2.into()
428 }
429
430 fn port_as_raw(&self) -> ctru_sys::u32_ {
431 ctru_sys::PORT_BOTH.into()
432 }
433
434 fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> {
435 let max_size = self.final_byte_length();
437 if buffer.len() < max_size {
438 return Err(Error::BufferTooShort {
439 provided: buffer.len(),
440 wanted: max_size,
441 });
442 }
443
444 let final_view = self.final_view_size();
445
446 let transfer_unit = unsafe {
448 let mut transfer_unit = 0;
449
450 ResultCode(ctru_sys::CAMU_GetMaxBytes(
451 &mut transfer_unit,
452 final_view.0,
453 final_view.1,
454 ))?;
455
456 transfer_unit
457 };
458
459 unsafe {
460 ResultCode(ctru_sys::CAMU_SetTransferBytes(
461 self.port_as_raw(),
462 transfer_unit,
463 final_view.0,
464 final_view.1,
465 ))?;
466 };
467
468 unsafe {
469 ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?;
470 ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
471 };
472
473 unsafe {
475 ResultCode(ctru_sys::CAMU_SynchronizeVsyncTiming(
476 ctru_sys::SELECT_OUT1.into(),
477 ctru_sys::SELECT_OUT2.into(),
478 ))?;
479 }
480
481 unsafe {
483 ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?;
484 };
485
486 let receive_event_1 = unsafe {
487 let mut completion_handle: Handle = 0;
488
489 ResultCode(ctru_sys::CAMU_SetReceiving(
490 &mut completion_handle,
491 buffer.as_mut_ptr().cast(),
492 ctru_sys::PORT_CAM1.into(),
493 (max_size / 2) as u32,
494 transfer_unit.try_into().unwrap(),
495 ))?;
496
497 completion_handle
498 };
499
500 let receive_event_2 = unsafe {
501 let mut completion_handle: Handle = 0;
502
503 ResultCode(ctru_sys::CAMU_SetReceiving(
504 &mut completion_handle,
505 buffer[max_size / 2..].as_mut_ptr().cast(),
506 ctru_sys::PORT_CAM2.into(),
507 (max_size / 2) as u32,
508 transfer_unit.try_into().unwrap(),
509 ))?;
510
511 completion_handle
512 };
513
514 unsafe {
515 let wait_result_1 = ResultCode(ctru_sys::svcWaitSynchronization(
517 receive_event_1,
518 timeout.as_nanos().try_into().unwrap(),
519 ));
520
521 let wait_result_2 = ResultCode(ctru_sys::svcWaitSynchronization(
522 receive_event_2,
523 timeout.as_nanos().try_into().unwrap(),
524 ));
525
526 let _ = ctru_sys::svcCloseHandle(receive_event_1); let _ = ctru_sys::svcCloseHandle(receive_event_2);
529
530 ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?;
532 ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
533 ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE.into()))?;
534
535 wait_result_1?;
536 wait_result_2?;
537 };
538
539 Ok(())
540 }
541}
542
543pub trait Camera: private::ConfigurableCamera {
545 fn camera_as_raw(&self) -> ctru_sys::u32_;
547
548 fn view_size(&self) -> ViewSize {
556 self.configuration().view_size
557 }
558
559 fn port_as_raw(&self) -> ctru_sys::u32_ {
561 ctru_sys::PORT_CAM1.into()
562 }
563
564 #[doc(alias = "CAMU_IsBusy")]
585 fn is_busy(&self) -> crate::Result<bool> {
586 unsafe {
587 let mut is_busy = false;
588 ResultCode(ctru_sys::CAMU_IsBusy(&mut is_busy, self.port_as_raw()))?;
589 Ok(is_busy)
590 }
591 }
592
593 fn final_byte_length(&self) -> usize {
619 let size = self.final_view_size();
620
621 let mut res: usize = (size.0 as usize * size.1 as usize) * std::mem::size_of::<i16>();
622
623 if self.port_as_raw() == ctru_sys::PORT_BOTH.into() {
625 res *= 2;
626 }
627
628 res
629 }
630
631 fn final_view_size(&self) -> (i16, i16) {
659 match self.trimming() {
660 Trimming::Centered { width, height } => (width, height),
661 Trimming::Off => self.view_size().into(),
662 }
663 }
664
665 fn trimming(&self) -> Trimming {
667 self.configuration().trimming
668 }
669
670 #[doc(alias = "CAMU_SetTrimming")]
682 fn set_trimming(&mut self, trimming: Trimming) -> crate::Result<()> {
683 match trimming {
684 Trimming::Centered { width, height } => unsafe {
685 let view_size: (i16, i16) = self.view_size().into();
686 let trim_size: (i16, i16) = (width, height);
687
688 assert!(
690 trim_size.0 <= view_size.0 && trim_size.1 <= view_size.1,
691 "trimmed view is bigger than the camera view",
692 );
693
694 ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
695
696 ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter(
697 self.port_as_raw(),
698 trim_size.0,
699 trim_size.1,
700 view_size.0,
701 view_size.1,
702 ))?;
703 },
704 Trimming::Off => unsafe {
705 ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
706 },
707 }
708
709 self.configuration_mut().trimming = trimming;
710
711 Ok(())
712 }
713
714 #[doc(alias = "CAMU_IsTrimming")]
716 fn is_trimming(&self) -> bool {
717 matches!(self.trimming(), Trimming::Off)
718 }
719
720 #[doc(alias = "CAMU_SetExposure")]
722 fn set_exposure(&mut self, exposure: i8) -> crate::Result<()> {
723 unsafe {
724 ResultCode(ctru_sys::CAMU_SetExposure(self.camera_as_raw(), exposure))?;
725 Ok(())
726 }
727 }
728
729 #[doc(alias = "CAMU_SetWhiteBalance")]
731 fn set_white_balance(&mut self, white_balance: WhiteBalance) -> crate::Result<()> {
732 unsafe {
733 ResultCode(ctru_sys::CAMU_SetWhiteBalance(
734 self.camera_as_raw(),
735 white_balance.into(),
736 ))?;
737 Ok(())
738 }
739 }
740
741 #[doc(alias = "CAMU_SetSharpness")]
743 fn set_sharpness(&mut self, sharpness: i8) -> crate::Result<()> {
744 unsafe {
745 ResultCode(ctru_sys::CAMU_SetSharpness(self.camera_as_raw(), sharpness))?;
746 Ok(())
747 }
748 }
749
750 #[doc(alias = "CAMU_SetAutoExposure")]
752 fn set_auto_exposure(&mut self, enabled: bool) -> crate::Result<()> {
753 unsafe {
754 ResultCode(ctru_sys::CAMU_SetAutoExposure(
755 self.camera_as_raw(),
756 enabled,
757 ))?;
758 Ok(())
759 }
760 }
761
762 #[doc(alias = "CAMU_IsAutoExposure")]
764 fn is_auto_exposure_enabled(&self) -> crate::Result<bool> {
765 unsafe {
766 let mut enabled = false;
767 ResultCode(ctru_sys::CAMU_IsAutoExposure(
768 &mut enabled,
769 self.camera_as_raw(),
770 ))?;
771 Ok(enabled)
772 }
773 }
774
775 #[doc(alias = "CAMU_FlipImage")]
777 fn flip_image(&mut self, flip: FlipMode) -> crate::Result<()> {
778 unsafe {
779 ResultCode(ctru_sys::CAMU_FlipImage(
780 self.camera_as_raw(),
781 flip.into(),
782 ctru_sys::CONTEXT_A,
783 ))?;
784 Ok(())
785 }
786 }
787
788 #[doc(alias = "CAMU_SetSize")]
794 fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
795 unsafe {
796 ResultCode(ctru_sys::CAMU_SetSize(
797 self.camera_as_raw(),
798 size.into(),
799 ctru_sys::CONTEXT_A,
800 ))?;
801 }
802
803 self.configuration_mut().view_size = size;
804
805 self.set_trimming(Trimming::Off)?;
806
807 Ok(())
808 }
809
810 #[doc(alias = "CAMU_SetFrameRate")]
812 fn set_frame_rate(&mut self, frame_rate: FrameRate) -> crate::Result<()> {
813 unsafe {
814 ResultCode(ctru_sys::CAMU_SetFrameRate(
815 self.camera_as_raw(),
816 frame_rate.into(),
817 ))?;
818 Ok(())
819 }
820 }
821
822 #[doc(alias = "CAMU_SetPhotoMode")]
824 fn set_photo_mode(&mut self, photo_mode: PhotoMode) -> crate::Result<()> {
825 unsafe {
826 ResultCode(ctru_sys::CAMU_SetPhotoMode(
827 self.camera_as_raw(),
828 photo_mode.into(),
829 ))?;
830 Ok(())
831 }
832 }
833
834 #[doc(alias = "CAMU_SetEffect")]
840 fn set_effect(&mut self, effect: Effect) -> crate::Result<()> {
841 unsafe {
842 ResultCode(ctru_sys::CAMU_SetEffect(
843 self.camera_as_raw(),
844 effect.into(),
845 ctru_sys::CONTEXT_A,
846 ))?;
847 Ok(())
848 }
849 }
850
851 #[doc(alias = "CAMU_SetContrast")]
853 fn set_contrast(&mut self, contrast: Contrast) -> crate::Result<()> {
854 unsafe {
855 ResultCode(ctru_sys::CAMU_SetContrast(
856 self.camera_as_raw(),
857 contrast.into(),
858 ))?;
859 Ok(())
860 }
861 }
862
863 #[doc(alias = "CAMU_SetLensCorrection")]
865 fn set_lens_correction(&mut self, lens_correction: LensCorrection) -> crate::Result<()> {
866 unsafe {
867 ResultCode(ctru_sys::CAMU_SetLensCorrection(
868 self.camera_as_raw(),
869 lens_correction.into(),
870 ))?;
871 Ok(())
872 }
873 }
874
875 #[doc(alias = "CAMU_SetOutputFormat")]
877 fn set_output_format(&mut self, format: OutputFormat) -> crate::Result<()> {
878 unsafe {
879 ResultCode(ctru_sys::CAMU_SetOutputFormat(
880 self.camera_as_raw(),
881 format.into(),
882 ctru_sys::CONTEXT_A,
883 ))?;
884 Ok(())
885 }
886 }
887
888 #[doc(alias = "CAMU_SetAutoExposureWindow")]
897 fn set_auto_exposure_window(
898 &mut self,
899 x: i16,
900 y: i16,
901 width: i16,
902 height: i16,
903 ) -> crate::Result<()> {
904 unsafe {
905 ResultCode(ctru_sys::CAMU_SetAutoExposureWindow(
906 self.camera_as_raw(),
907 x,
908 y,
909 width,
910 height,
911 ))?;
912 Ok(())
913 }
914 }
915
916 #[doc(alias = "CAMU_SetAutoWhiteBalanceWindow")]
929 fn set_auto_white_balance_window(
930 &mut self,
931 x: i16,
932 y: i16,
933 width: i16,
934 height: i16,
935 ) -> crate::Result<()> {
936 unsafe {
937 ResultCode(ctru_sys::CAMU_SetAutoWhiteBalanceWindow(
938 self.camera_as_raw(),
939 x,
940 y,
941 width,
942 height,
943 ))?;
944 Ok(())
945 }
946 }
947
948 #[doc(alias = "CAMU_SetNoiseFilter")]
950 fn set_noise_filter(&mut self, enabled: bool) -> crate::Result<()> {
951 unsafe {
952 ResultCode(ctru_sys::CAMU_SetNoiseFilter(self.camera_as_raw(), enabled))?;
953 Ok(())
954 }
955 }
956
957 #[doc(alias = "CAMU_SetImageQualityCalibrationData")]
959 fn set_image_quality_calibration(
960 &mut self,
961 data: ImageQualityCalibration,
962 ) -> crate::Result<()> {
963 unsafe {
964 ResultCode(ctru_sys::CAMU_SetImageQualityCalibrationData(data.0))?;
965 Ok(())
966 }
967 }
968
969 #[doc(alias = "CAMU_GetImageQualityCalibrationData")]
971 fn image_quality_calibration(&self) -> crate::Result<ImageQualityCalibration> {
972 unsafe {
973 let mut data = ImageQualityCalibration::default();
974 ResultCode(ctru_sys::CAMU_GetImageQualityCalibrationData(&mut data.0))?;
975 Ok(data)
976 }
977 }
978
979 fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> {
1021 let max_size = self.final_byte_length();
1023 if buffer.len() < max_size {
1024 return Err(Error::BufferTooShort {
1025 provided: buffer.len(),
1026 wanted: max_size,
1027 });
1028 }
1029
1030 let final_view = self.final_view_size();
1031
1032 let transfer_unit = unsafe {
1034 let mut transfer_unit = 0;
1035
1036 ResultCode(ctru_sys::CAMU_GetMaxBytes(
1037 &mut transfer_unit,
1038 final_view.0,
1039 final_view.1,
1040 ))?;
1041
1042 transfer_unit
1043 };
1044
1045 unsafe {
1046 ResultCode(ctru_sys::CAMU_SetTransferBytes(
1047 self.port_as_raw(),
1048 transfer_unit,
1049 final_view.0,
1050 final_view.1,
1051 ))?;
1052 };
1053
1054 unsafe {
1055 ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?;
1056 ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
1057 };
1058
1059 let receive_event = unsafe {
1060 let mut completion_handle: Handle = 0;
1061
1062 ResultCode(ctru_sys::CAMU_SetReceiving(
1063 &mut completion_handle,
1064 buffer.as_mut_ptr().cast(),
1065 self.port_as_raw(),
1066 max_size as u32,
1067 transfer_unit.try_into().unwrap(),
1068 ))?;
1069
1070 completion_handle
1071 };
1072
1073 unsafe {
1075 ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?;
1076 };
1077
1078 unsafe {
1079 let wait_result = ResultCode(ctru_sys::svcWaitSynchronization(
1081 receive_event,
1082 timeout.as_nanos().try_into().unwrap(),
1083 ));
1084
1085 let _ = ctru_sys::svcCloseHandle(receive_event); ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?;
1090 ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
1091 ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE.into()))?;
1092
1093 wait_result?;
1094 };
1095
1096 Ok(())
1097 }
1098}
1099
1100impl Trimming {
1101 pub fn new_centered(width: i16, height: i16) -> Self {
1108 assert!((width * height) % 128 == 0);
1110
1111 Self::Centered { width, height }
1112 }
1113
1114 pub fn new_centered_with_view(size: ViewSize) -> Self {
1116 let size: (i16, i16) = size.into();
1117
1118 Self::Centered {
1119 width: size.0,
1120 height: size.1,
1121 }
1122 }
1123}
1124
1125impl Cam {
1126 #[doc(alias = "camInit")]
1154 pub fn new() -> crate::Result<Cam> {
1155 let _service_handler = ServiceReference::new(
1156 &CAM_ACTIVE,
1157 || {
1158 ResultCode(unsafe { ctru_sys::camInit() })?;
1159
1160 Ok(())
1161 },
1162 || unsafe {
1163 ctru_sys::camExit();
1164 },
1165 )?;
1166
1167 let configuration = Configuration::new();
1168
1169 let mut inner_cam = InwardCam { configuration };
1170 let mut outer_right_cam = OutwardRightCam { configuration };
1171 let mut outer_left_cam = OutwardLeftCam { configuration };
1172 let mut both_outer_cams = BothOutwardCam { configuration };
1173
1174 inner_cam.set_view_size(ViewSize::TopLCD)?;
1175 outer_right_cam.set_view_size(ViewSize::TopLCD)?;
1176 outer_left_cam.set_view_size(ViewSize::TopLCD)?;
1177 both_outer_cams.set_view_size(ViewSize::TopLCD)?;
1178
1179 Ok(Cam {
1180 _service_handler,
1181 inner_cam,
1182 outer_right_cam,
1183 outer_left_cam,
1184 both_outer_cams,
1185 })
1186 }
1187
1188 #[doc(alias = "CAMU_PlayShutterSound")]
1213 pub fn play_shutter_sound(&self, sound: ShutterSound) -> crate::Result<()> {
1214 unsafe {
1215 ResultCode(ctru_sys::CAMU_PlayShutterSound(sound.into()))?;
1216 Ok(())
1217 }
1218 }
1219}
1220
1221impl TryFrom<FramebufferFormat> for OutputFormat {
1222 type Error = ();
1223
1224 fn try_from(value: FramebufferFormat) -> Result<Self, Self::Error> {
1225 match value {
1226 FramebufferFormat::Rgb565 => Ok(OutputFormat::Rgb565),
1227 _ => Err(()),
1228 }
1229 }
1230}
1231
1232impl TryFrom<OutputFormat> for FramebufferFormat {
1233 type Error = ();
1234
1235 fn try_from(value: OutputFormat) -> Result<Self, Self::Error> {
1236 match value {
1237 OutputFormat::Rgb565 => Ok(FramebufferFormat::Rgb565),
1238 _ => Err(()),
1239 }
1240 }
1241}
1242
1243impl From<ViewSize> for (i16, i16) {
1244 fn from(value: ViewSize) -> Self {
1245 match value {
1246 ViewSize::TopLCD => (400, 240),
1247 ViewSize::BottomLCD => (320, 240),
1248 ViewSize::Vga => (640, 480),
1249 ViewSize::QQVga => (160, 120),
1250 ViewSize::Cif => (352, 288),
1251 ViewSize::QCif => (176, 144),
1252 ViewSize::DS => (256, 192),
1253 ViewSize::DSX4 => (512, 384),
1254 }
1255 }
1256}
1257
1258from_impl!(FlipMode, ctru_sys::CAMU_Flip);
1259from_impl!(ViewSize, ctru_sys::CAMU_Size);
1260from_impl!(FrameRate, ctru_sys::CAMU_FrameRate);
1261from_impl!(WhiteBalance, ctru_sys::CAMU_WhiteBalance);
1262from_impl!(PhotoMode, ctru_sys::CAMU_PhotoMode);
1263from_impl!(Effect, ctru_sys::CAMU_Effect);
1264from_impl!(Contrast, ctru_sys::CAMU_Contrast);
1265from_impl!(LensCorrection, ctru_sys::CAMU_LensCorrection);
1266from_impl!(OutputFormat, ctru_sys::CAMU_OutputFormat);
1267from_impl!(ShutterSound, ctru_sys::CAMU_ShutterSoundType);