Browse Source

Merge pull request #76 from ian-h-chamberlain/feature/split-top-screen

First attempt at splitting top screen types
pull/87/head
Meziu 2 years ago committed by GitHub
parent
commit
5ff84bbfea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      ctru-rs/examples/camera-image.rs
  2. 5
      ctru-rs/examples/file-explorer.rs
  3. 70
      ctru-rs/examples/gfx-3d-mode.rs
  4. 179
      ctru-rs/src/gfx.rs

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

@ -1,5 +1,5 @@
use ctru::console::Console; use ctru::console::Console;
use ctru::gfx::{Gfx, Screen, Side}; use ctru::gfx::{Gfx, Screen};
use ctru::services::cam::{Cam, CamOutputFormat, CamShutterSoundType, CamSize, Camera}; use ctru::services::cam::{Cam, CamOutputFormat, CamShutterSoundType, CamSize, Camera};
use ctru::services::hid::KeyPad; use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid}; use ctru::services::{Apt, Hid};
@ -88,7 +88,7 @@ fn main() {
unsafe { unsafe {
gfx.top_screen gfx.top_screen
.borrow_mut() .borrow_mut()
.get_raw_framebuffer(Side::Left) .get_raw_framebuffer()
.ptr .ptr
.copy_from(img.as_ptr(), img.len()); .copy_from(img.as_ptr(), img.len());
} }

5
ctru-rs/examples/file-explorer.rs

@ -32,8 +32,9 @@ struct FileExplorer<'a> {
impl<'a> FileExplorer<'a> { impl<'a> FileExplorer<'a> {
fn init(apt: &'a Apt, hid: &'a Hid, gfx: &'a Gfx) -> Self { fn init(apt: &'a Apt, hid: &'a Hid, gfx: &'a Gfx) -> Self {
gfx.top_screen.borrow_mut().set_wide_mode(true); let mut top_screen = gfx.top_screen.borrow_mut();
let console = Console::init(gfx.top_screen.borrow_mut()); top_screen.set_wide_mode(true);
let console = Console::init(top_screen);
FileExplorer { FileExplorer {
apt, apt,

70
ctru-rs/examples/gfx-3d-mode.rs

@ -0,0 +1,70 @@
use ctru::gfx::{Screen, Side, TopScreen3D};
use ctru::prelude::*;
/// See `graphics-bitmap.rs` for details on how the image is generated.
///
/// WARNING: this example uses 3D mode in a rather unnatural way, and should
/// probably not be viewed for too long or at all if you are photosensitive.
const IMAGE: &[u8] = include_bytes!("assets/ferris.rgb");
static ZERO: &[u8] = &[0; IMAGE.len()];
fn main() {
ctru::init();
let gfx = Gfx::init().expect("Couldn't obtain GFX controller");
let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller");
let _console = Console::init(gfx.bottom_screen.borrow_mut());
println!("Press Start to exit.\nPress A to switch sides (be sure to have 3D mode enabled).");
gfx.top_screen.borrow_mut().set_double_buffering(true);
let top_screen = TopScreen3D::from(&gfx.top_screen);
let (mut left, mut right) = top_screen.split_mut();
let mut current_side = Side::Left;
// Main loop
while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input();
if hid.keys_down().contains(KeyPad::KEY_START) {
break;
}
let left_buf = left.get_raw_framebuffer();
let right_buf = right.get_raw_framebuffer();
// Clear both buffers every time, in case the user switches sides this loop
unsafe {
left_buf.ptr.copy_from(ZERO.as_ptr(), ZERO.len());
right_buf.ptr.copy_from(ZERO.as_ptr(), ZERO.len());
}
if hid.keys_down().contains(KeyPad::KEY_A) {
// flip which buffer we're writing to
current_side = match current_side {
Side::Left => Side::Right,
Side::Right => Side::Left,
};
}
let buf = match current_side {
Side::Left => left_buf.ptr,
Side::Right => right_buf.ptr,
};
unsafe {
buf.copy_from(IMAGE.as_ptr(), IMAGE.len());
}
// Flush and swap framebuffers
gfx.flush_buffers();
gfx.swap_buffers();
//Wait for VBlank
gfx.wait_for_vblank();
}
}

179
ctru-rs/src/gfx.rs

@ -1,7 +1,7 @@
//! LCD screens manipulation helper //! LCD screens manipulation helper
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::cell::RefCell; use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Mutex; use std::sync::Mutex;
@ -9,11 +9,45 @@ use crate::error::Result;
use crate::services::gspgpu::{self, FramebufferFormat}; use crate::services::gspgpu::{self, FramebufferFormat};
use crate::services::ServiceReference; use crate::services::ServiceReference;
/// Trait implemented by TopScreen and BottomScreen for common methods mod private {
pub trait Screen { use super::{BottomScreen, TopScreen, TopScreenLeft, TopScreenRight};
/// Returns the libctru value for the Screen kind
pub trait Sealed {}
impl Sealed for TopScreen {}
impl Sealed for TopScreenLeft {}
impl Sealed for TopScreenRight {}
impl Sealed for BottomScreen {}
}
/// This trait is implemented by the screen structs for working with frame buffers and
/// drawing to the screens. Graphics-related code can be made generic over this
/// trait to work with any of the given screens.
pub trait Screen: private::Sealed {
/// Returns the `libctru` value for the Screen kind.
fn as_raw(&self) -> ctru_sys::gfxScreen_t; fn as_raw(&self) -> ctru_sys::gfxScreen_t;
/// Returns the Screen side (left or right).
fn side(&self) -> Side;
/// Returns a [`RawFrameBuffer`] for the screen.
///
/// Note that the pointer of the framebuffer returned by this function can
/// change after each call to this function if double buffering is enabled.
fn get_raw_framebuffer(&mut self) -> RawFrameBuffer {
let mut width = 0;
let mut height = 0;
let ptr = unsafe {
ctru_sys::gfxGetFramebuffer(self.as_raw(), self.side().into(), &mut width, &mut height)
};
RawFrameBuffer {
ptr,
width,
height,
screen: PhantomData,
}
}
/// Sets whether to use double buffering. Enabled by default. /// Sets whether to use double buffering. Enabled by default.
/// ///
/// Note that even when double buffering is disabled, one should still use the `swap_buffers` /// Note that even when double buffering is disabled, one should still use the `swap_buffers`
@ -33,10 +67,26 @@ pub trait Screen {
} }
} }
#[non_exhaustive] /// The top screen. Mutable access to this struct is required to write to the top
pub struct TopScreen; /// screen's frame buffer. To enable 3D mode, it can be converted into a [`TopScreen3D`].
pub struct TopScreen {
left: TopScreenLeft,
right: TopScreenRight,
}
/// A helper container for both sides of the top screen. Once the [`TopScreen`] is
/// converted into this, 3D mode will be enabled until this struct is dropped.
pub struct TopScreen3D<'top_screen> {
screen: &'top_screen RefCell<TopScreen>,
}
struct TopScreenLeft;
struct TopScreenRight;
#[non_exhaustive] #[non_exhaustive]
/// The bottom screen. Mutable access to this struct is required to write to the
/// bottom screen's frame buffer.
pub struct BottomScreen; pub struct BottomScreen;
/// Representation of a framebuffer for one [`Side`] of the top screen, or the /// Representation of a framebuffer for one [`Side`] of the top screen, or the
@ -88,7 +138,7 @@ impl Gfx {
bottom_fb_fmt: FramebufferFormat, bottom_fb_fmt: FramebufferFormat,
use_vram_buffers: bool, use_vram_buffers: bool,
) -> Result<Self> { ) -> Result<Self> {
let _service_handler = ServiceReference::new( let handler = ServiceReference::new(
&GFX_ACTIVE, &GFX_ACTIVE,
false, false,
|| unsafe { || unsafe {
@ -100,14 +150,15 @@ impl Gfx {
)?; )?;
Ok(Self { Ok(Self {
top_screen: RefCell::new(TopScreen), top_screen: RefCell::new(TopScreen::new()),
bottom_screen: RefCell::new(BottomScreen), bottom_screen: RefCell::new(BottomScreen),
_service_handler, _service_handler: handler,
}) })
} }
/// Creates a new Gfx instance with default init values /// Creates a new [Gfx] instance with default init values
/// It's the same as calling: `Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false) /// It's the same as calling:
/// `Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false)`
pub fn init() -> Result<Self> { pub fn init() -> Result<Self> {
Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false) Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false)
} }
@ -139,80 +190,106 @@ impl Gfx {
} }
} }
impl TopScreen { impl TopScreen3D<'_> {
/// Enable or disable the 3D stereoscopic effect /// Immutably borrow the two sides of the screen as `(left, right)`.
pub fn set_3d_enabled(&mut self, enabled: bool) { pub fn split(&self) -> (Ref<dyn Screen>, Ref<dyn Screen>) {
Ref::map_split(self.screen.borrow(), |screen| {
(&screen.left as _, &screen.right as _)
})
}
/// Mutably borrow the two sides of the screen as `(left, right)`.
pub fn split_mut(&self) -> (RefMut<dyn Screen>, RefMut<dyn Screen>) {
RefMut::map_split(self.screen.borrow_mut(), |screen| {
(&mut screen.left as _, &mut screen.right as _)
})
}
}
impl<'top_screen> From<&'top_screen RefCell<TopScreen>> for TopScreen3D<'top_screen> {
fn from(top_screen: &'top_screen RefCell<TopScreen>) -> Self {
unsafe {
ctru_sys::gfxSet3D(true);
}
TopScreen3D { screen: top_screen }
}
}
impl Drop for TopScreen3D<'_> {
fn drop(&mut self) {
unsafe { unsafe {
ctru_sys::gfxSet3D(enabled); ctru_sys::gfxSet3D(false);
}
}
}
impl TopScreen {
fn new() -> Self {
Self {
left: TopScreenLeft,
right: TopScreenRight,
} }
} }
/// Enable or disable the wide screen mode (top screen). /// Enable or disable wide mode on the top screen.
/// This only works when 3D is disabled. pub fn set_wide_mode(&mut self, enable: bool) {
pub fn set_wide_mode(&mut self, enabled: bool) {
unsafe { unsafe {
ctru_sys::gfxSetWide(enabled); ctru_sys::gfxSetWide(enable);
} }
} }
/// Get the status of wide screen mode. /// Returns whether or not wide mode is enabled on the top screen.
pub fn get_wide_mode(&self) -> bool { pub fn get_wide_mode(&self) -> bool {
unsafe { ctru_sys::gfxIsWide() } unsafe { ctru_sys::gfxIsWide() }
} }
/// Returns a [`RawFrameBuffer`] for the given [`Side`] of the top screen.
///
/// Note that the pointer of the framebuffer returned by this function can
/// change after each call to this function if double buffering is enabled.
pub fn get_raw_framebuffer(&mut self, side: Side) -> RawFrameBuffer {
RawFrameBuffer::for_screen_side(self, side)
} }
impl Screen for TopScreen {
fn as_raw(&self) -> ctru_sys::gfxScreen_t {
self.left.as_raw()
} }
impl BottomScreen { fn side(&self) -> Side {
/// Returns a [`RawFrameBuffer`] for the bottom screen. self.left.side()
///
/// Note that the pointer of the framebuffer returned by this function can
/// change after each call to this function if double buffering is enabled.
pub fn get_raw_framebuffer(&mut self) -> RawFrameBuffer {
RawFrameBuffer::for_screen_side(self, Side::Left)
} }
} }
impl<'screen> RawFrameBuffer<'screen> { impl Screen for TopScreenLeft {
fn for_screen_side(screen: &'screen mut dyn Screen, side: Side) -> Self { fn as_raw(&self) -> ctru_sys::gfxScreen_t {
let mut width = 0; ctru_sys::GFX_TOP
let mut height = 0;
let ptr = unsafe {
ctru_sys::gfxGetFramebuffer(screen.as_raw(), side.into(), &mut width, &mut height)
};
Self {
ptr,
width,
height,
screen: PhantomData,
} }
fn side(&self) -> Side {
Side::Left
} }
} }
impl Screen for TopScreen { impl Screen for TopScreenRight {
fn as_raw(&self) -> ctru_sys::gfxScreen_t { fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_TOP ctru_sys::GFX_TOP
} }
fn side(&self) -> Side {
Side::Right
}
} }
impl Screen for BottomScreen { impl Screen for BottomScreen {
fn as_raw(&self) -> ctru_sys::gfxScreen_t { fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_BOTTOM ctru_sys::GFX_BOTTOM
} }
fn side(&self) -> Side {
Side::Left
}
} }
impl From<Side> for ctru_sys::gfx3dSide_t { impl From<Side> for ctru_sys::gfx3dSide_t {
fn from(s: Side) -> ctru_sys::gfx3dSide_t { fn from(s: Side) -> ctru_sys::gfx3dSide_t {
use self::Side::*;
match s { match s {
Left => ctru_sys::GFX_LEFT, Side::Left => ctru_sys::GFX_LEFT,
Right => ctru_sys::GFX_RIGHT, Side::Right => ctru_sys::GFX_RIGHT,
} }
} }
} }
@ -225,6 +302,6 @@ mod tests {
#[test] #[test]
fn gfx_duplicate() { fn gfx_duplicate() {
// We don't need to build a `Gfx` because the test runner has one already // We don't need to build a `Gfx` because the test runner has one already
assert!(matches!(Gfx::init(), Err(Error::ServiceAlreadyActive))) assert!(matches!(Gfx::init(), Err(Error::ServiceAlreadyActive)));
} }
} }

Loading…
Cancel
Save