diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 1484b64..3ac66d1 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Ronald Kinard "] description = "A safe wrapper around smealum's ctrulib." license = "https://en.wikipedia.org/wiki/Zlib_License" name = "ctru-rs" -version = "0.7.0" +version = "0.7.1" [lib] crate-type = ["rlib"] diff --git a/ctru-rs/src/gfx.rs b/ctru-rs/src/gfx.rs index 4f55271..749a69a 100644 --- a/ctru-rs/src/gfx.rs +++ b/ctru-rs/src/gfx.rs @@ -1,118 +1,138 @@ +//! LCD screens manipulation helper + use std::default::Default; use std::ops::Drop; -use services::gspgpu::FramebufferFormat; +use services::gspgpu::{self, FramebufferFormat}; +/// A handle to libctru's gfx module. This module is a wrapper around the GSPGPU service that +/// provides helper functions and utilities for software rendering. +/// +/// The service exits when this struct is dropped. pub struct Gfx(()); -#[derive(Copy, Clone)] +/// Available screens on the 3DS +#[derive(Copy, Clone, Debug)] pub enum Screen { + /// The top screen Top, + /// The bottom screen Bottom, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] +/// Side of top screen framebuffer +/// +/// The top screen of the 3DS can have two separate sets of framebuffers to support its 3D functionality pub enum Side { + /// The left framebuffer. This framebuffer is also the one used when 3D is disabled Left, + /// The right framebuffer Right, } -impl From<::libctru::gfxScreen_t> for Screen { - fn from(g: ::libctru::gfxScreen_t) -> Screen { - use self::Screen::*; - match g { - ::libctru::GFX_TOP => Top, - ::libctru::GFX_BOTTOM => Bottom, - _ => unreachable!(), - } - } -} - -impl From for ::libctru::gfxScreen_t { - fn from(g: Screen) -> ::libctru::gfxScreen_t { - use self::Screen::*; - match g { - Top => ::libctru::GFX_TOP, - Bottom => ::libctru::GFX_BOTTOM, - } - } -} - -impl From<::libctru::gfx3dSide_t> for Side { - fn from(s: ::libctru::gfx3dSide_t) -> Side { - use self::Side::*; - match s { - ::libctru::GFX_LEFT => Left, - ::libctru::GFX_RIGHT => Right, - _ => unreachable!(), - } - } -} - -impl From for ::libctru::gfx3dSide_t { - fn from(s: Side) -> ::libctru::gfx3dSide_t { - use self::Side::*; - match s { - Left => ::libctru::GFX_LEFT, - Right => ::libctru::GFX_RIGHT, - } +impl Gfx { + /// Initialize the Gfx module with the chosen framebuffer formats for the top and bottom + /// screens + /// + /// Use `Gfx::default()` instead of this function to initialize the module with default parameters + pub fn new( + top_fb_fmt: FramebufferFormat, bottom_fb_fmt: FramebufferFormat, use_vram_buffers: bool) -> Self { + unsafe { ::libctru::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), use_vram_buffers); } + Gfx(()) } -} -impl Gfx { + /// Enable or disable the 3D stereoscopic effect pub fn set_3d_enabled(&self, enabled: bool) { unsafe { ::libctru::gfxSet3D(enabled) } } - pub fn get_framebuffer(&self, screen: Screen, side: Side) -> (&'static mut [u8], u16, u16) { + /// Sets whether to use double buffering. Enabled by default. + /// + /// Note that even when double buffering is disabled, one should still use the `swap_buffers` + /// method on each frame to keep the gsp configuration up to date + pub fn set_double_buffering(&self, screen: Screen, enabled: bool) { unsafe { - use std::slice::from_raw_parts_mut; - - let mut w: u16 = 0; - let mut h: u16 = 0; - let buf: *mut u8 = ::libctru::gfxGetFramebuffer(screen.into(), - side.into(), - &mut w as *mut u16, - &mut h as &mut u16); - - let fbfmt = self.get_framebuffer_format(screen); - - (from_raw_parts_mut(buf, (w as usize * h as usize) * fbfmt.pixel_depth_bytes()), w, h) + ::libctru::gfxSetDoubleBuffering(screen.into(), enabled) } } + /// Flushes the current framebuffers pub fn flush_buffers(&self) { unsafe { ::libctru::gfxFlushBuffers() }; } + /// Swaps the framebuffers and sets the gsp state + /// + /// Use this function when working with software rendering pub fn swap_buffers(&self) { unsafe { ::libctru::gfxSwapBuffers() }; } + /// Swaps the framebuffers without manipulating the gsp state + /// + /// Use this function when working with GPU rendering pub fn swap_buffers_gpu(&self) { unsafe { ::libctru::gfxSwapBuffersGpu() }; } + /// Waits for the vertical blank interrupt + /// + /// Use this to synchronize your application with the refresh rate of the LCD screens + pub fn wait_for_vblank(&self) { + gspgpu::wait_for_event(gspgpu::Event::VBlank0, true); + + } + + /// Gets the framebuffer format for a screen pub fn get_framebuffer_format(&self, screen: Screen) -> FramebufferFormat { unsafe { ::libctru::gfxGetScreenFormat(screen.into()).into() } } - pub fn set_framebuffer_format(&self, screen: Screen, - fmt: FramebufferFormat) { + /// Change the framebuffer format for a screen + pub fn set_framebuffer_format(&self, screen: Screen, fmt: FramebufferFormat) { unsafe { ::libctru::gfxSetScreenFormat(screen.into(), fmt.into()) } } - pub fn set_double_buffering(&self, screen: Screen, enabled: bool) { + /// Returns a tuple containing a pointer to the specifified framebuffer (as determined by the + /// provided `Screen` and `Side`), the width of the framebuffer in pixels, and the height of + /// the framebuffer in pixels + /// + /// Note that the pointer returned by this function can change after each call to this function + /// if double buffering is enabled + pub fn get_raw_framebuffer(&self, screen: Screen, side: Side) -> (*mut u8, u16, u16) { unsafe { - ::libctru::gfxSetDoubleBuffering(screen.into(), enabled) + let mut width: u16 = 0; + let mut height: u16 = 0; + let buf: *mut u8 = ::libctru::gfxGetFramebuffer( + screen.into(), + side.into(), + &mut width, + &mut height, + ); + (buf, width, height) } } +} - pub fn wait_for_vblank(&self) { - unsafe { - ::libctru::gspWaitForEvent(::libctru::GSPGPU_EVENT_VBlank0, true) +impl From for ::libctru::gfxScreen_t { + fn from(g: Screen) -> ::libctru::gfxScreen_t { + use self::Screen::*; + match g { + Top => ::libctru::GFX_TOP, + Bottom => ::libctru::GFX_BOTTOM, + } + } +} + +impl From for ::libctru::gfx3dSide_t { + fn from(s: Side) -> ::libctru::gfx3dSide_t { + use self::Side::*; + match s { + Left => ::libctru::GFX_LEFT, + Right => ::libctru::GFX_RIGHT, } } } diff --git a/ctru-rs/src/services/gspgpu.rs b/ctru-rs/src/services/gspgpu.rs index b36cb36..b047493 100644 --- a/ctru-rs/src/services/gspgpu.rs +++ b/ctru-rs/src/services/gspgpu.rs @@ -1,5 +1,8 @@ +//! GSPGPU service + use std::convert::From; +#[derive(Copy, Clone, Debug)] pub enum Event { Psc0, Psc1, @@ -10,31 +13,46 @@ pub enum Event { DMA, } -#[derive(Copy, Clone)] +/// The different framebuffer formats supported by the 3DS +#[derive(Copy, Clone, Debug)] pub enum FramebufferFormat { + /// RGBA8. 4 bytes per pixel Rgba8, + /// BGR8. 3 bytes per pixel Bgr8, + /// RGB565. 2 bytes per pixel Rgb565, + /// RGB5A1. 2 bytes per pixel Rgb5A1, + /// RGBA4. 2 bytes per pixel Rgba4, } impl FramebufferFormat { + /// Returns the number of bytes per pixel used by this FramebufferFormat pub fn pixel_depth_bytes(&self) -> usize { use self::FramebufferFormat::*; match *self { - Rgba8 => 4usize, - Bgr8 => 3usize, - Rgb565 => 2usize, - Rgb5A1 => 2usize, - Rgba4 => 2usize, + Rgba8 => 4, + Bgr8 => 3, + Rgb565 => 2, + Rgb5A1 => 2, + Rgba4 => 2, } } } +/// Waits for a GSPGPU event to occur. +/// +/// `discard_current` determines whether to discard the current event and wait for the next event +pub fn wait_for_event(ev: Event, discard_current: bool) { + unsafe { + ::libctru::gspWaitForEvent(ev.into(), discard_current); + } +} + impl From<::libctru::GSPGPU_FramebufferFormats> for FramebufferFormat { - #[inline] - fn from(g: ::libctru::GSPGPU_FramebufferFormats) -> FramebufferFormat { + fn from(g: ::libctru::GSPGPU_FramebufferFormats) -> Self { use self::FramebufferFormat::*; match g { ::libctru::GSP_RGBA8_OES => Rgba8, @@ -48,8 +66,7 @@ impl From<::libctru::GSPGPU_FramebufferFormats> for FramebufferFormat { } impl From for ::libctru::GSPGPU_FramebufferFormats { - #[inline] - fn from(g: FramebufferFormat) -> ::libctru::GSPGPU_FramebufferFormats { + fn from(g: FramebufferFormat) -> Self { use self::FramebufferFormat::*; match g { Rgba8 => ::libctru::GSP_RGBA8_OES, @@ -61,34 +78,17 @@ impl From for ::libctru::GSPGPU_FramebufferFormats { } } -fn to_raw_event(ev: Event) -> ::libctru::GSPGPU_Event { - use self::Event::*; - - match ev { - Psc0 => ::libctru::GSPGPU_EVENT_PSC0, - Psc1 => ::libctru::GSPGPU_EVENT_PSC1, - VBlank0 => ::libctru::GSPGPU_EVENT_VBlank0, - VBlank1 => ::libctru::GSPGPU_EVENT_VBlank1, - PPF => ::libctru::GSPGPU_EVENT_PPF, - P3D => ::libctru::GSPGPU_EVENT_P3D, - DMA => ::libctru::GSPGPU_EVENT_DMA, - } -} - -/// Sleep until GSP event fires. -/// -/// # Examples -/// -/// Wait for VBlank. -/// -/// ``` -/// use ctru::services::apt; -/// apt::main_loop(|| { -/// wait_for_event(Event::VBlank0); -/// }); -pub fn wait_for_event(ev: Event) -> () { - unsafe { - // TODO second argument? - ::libctru::gspWaitForEvent(to_raw_event(ev), false); +impl From for ::libctru::GSPGPU_Event { + fn from(ev: Event) -> Self { + use self::Event::*; + match ev { + Psc0 => ::libctru::GSPGPU_EVENT_PSC0, + Psc1 => ::libctru::GSPGPU_EVENT_PSC1, + VBlank0 => ::libctru::GSPGPU_EVENT_VBlank0, + VBlank1 => ::libctru::GSPGPU_EVENT_VBlank1, + PPF => ::libctru::GSPGPU_EVENT_PPF, + P3D => ::libctru::GSPGPU_EVENT_P3D, + DMA => ::libctru::GSPGPU_EVENT_DMA, + } } }