Browse Source

Merge branch 'improve/api' into feat/flush-swap-screen-traits

pull/118/head
Andrea Ciliberti 2 years ago
parent
commit
a3fc356d6a
  1. 1
      .github/CODEOWNERS
  2. 46
      .github/actions/setup/action.yml
  3. 56
      .github/workflows/ci.yml
  4. 57
      ctru-rs/examples/graphics-bitmap.rs
  5. 49
      ctru-rs/examples/touch-screen.rs
  6. 28
      ctru-rs/src/applets/mii_selector.rs
  7. 24
      ctru-rs/src/services/cfgu.rs
  8. 57
      ctru-rs/src/services/fs.rs
  9. 58
      ctru-rs/src/services/hid.rs

1
.github/CODEOWNERS

@ -0,0 +1 @@
* @rust3ds/active

46
.github/actions/setup/action.yml

@ -0,0 +1,46 @@
name: Setup
description: Set up CI environment for Rust + 3DS development
inputs:
toolchain:
description: The Rust toolchain to use for the steps
required: true
default: nightly
runs:
using: composite
steps:
# https://github.com/nektos/act/issues/917#issuecomment-1074421318
- if: ${{ env.ACT }}
shell: bash
name: Hack container for local development
run: |
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
- name: Setup default Rust toolchain
# Use this helper action so we get matcher support
# https://github.com/actions-rust-lang/setup-rust-toolchain/pull/15
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: clippy, rustfmt, rust-src
toolchain: ${{ inputs.toolchain }}
- name: Install build tools for host
shell: bash
run: sudo apt-get update && sudo apt-get install -y build-essential
- name: Install cargo-3ds
uses: actions-rs/cargo@v1
with:
command: install
# TODO: this should probably just be a released version from crates.io
# once cargo-3ds gets published somewhere...
args: >-
--git https://github.com/rust3ds/cargo-3ds
--rev 78a652fdfb01e2614a792d1a56b10c980ee1dae9
- name: Set PATH to include devkitARM
shell: bash
# For some reason devkitARM/bin is not part of the default PATH in the container
run: echo "${DEVKITARM}/bin" >> $GITHUB_PATH

56
.github/workflows/ci.yml

@ -12,6 +12,8 @@ on:
env: env:
# https://blog.rust-lang.org/2022/06/22/sparse-registry-testing.html # https://blog.rust-lang.org/2022/06/22/sparse-registry-testing.html
CARGO_UNSTABLE_SPARSE_REGISTRY: "true" CARGO_UNSTABLE_SPARSE_REGISTRY: "true"
# actions-rust-lang/setup-rust-toolchain sets some default RUSTFLAGS
RUSTFLAGS: ""
jobs: jobs:
lint: lint:
@ -22,41 +24,24 @@ jobs:
- nightly-2023-01-13 - nightly-2023-01-13
# Check for breakage on latest nightly # Check for breakage on latest nightly
- nightly - nightly
# But if latest nightly fails, allow the workflow to continue # But if latest nightly fails, allow the workflow to continue
continue-on-error: ${{ matrix.toolchain == 'nightly' }} continue-on-error: ${{ matrix.toolchain == 'nightly' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: devkitpro/devkitarm container: devkitpro/devkitarm
steps: steps:
# https://github.com/nektos/act/issues/917#issuecomment-1074421318
- if: ${{ env.ACT }}
name: Hack container for local development
run: |
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
- name: Checkout branch - name: Checkout branch
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup default Rust toolchain - uses: ./.github/actions/setup
uses: actions-rs/toolchain@v1
with: with:
components: clippy, rustfmt, rust-src
profile: minimal
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
default: true
- name: Install build tools for host - name: Hide duplicate warnings from lint job
run: sudo apt-get update && sudo apt-get install -y build-essential if: ${{ matrix.toolchain == 'nightly' }}
run: |
- name: Install cargo-3ds echo "::remove-matcher owner=clippy::"
uses: actions-rs/cargo@v1 echo "::remove-matcher owner=rustfmt::"
with:
command: install
# TODO: this should probably just be a released version from crates.io
# once cargo-3ds gets published somewhere...
args: >-
--git https://github.com/rust3ds/cargo-3ds
--rev 7b70b6b26c4740b9a10ab85b832ee73c41142bbb
- name: Check formatting - name: Check formatting
run: cargo fmt --all --verbose -- --check run: cargo fmt --all --verbose -- --check
@ -68,5 +53,28 @@ jobs:
# feature, but https://github.com/actions/runner/issues/2341 means we # feature, but https://github.com/actions/runner/issues/2341 means we
# can't have both that *and* colored output. # can't have both that *and* colored output.
doctests:
strategy:
matrix:
toolchain:
- nightly-2023-01-13
- nightly
continue-on-error: ${{ matrix.toolchain == 'nightly' }}
runs-on: ubuntu-latest
container: devkitpro/devkitarm
steps:
- name: Checkout branch
uses: actions/checkout@v2
- uses: ./.github/actions/setup
with:
toolchain: ${{ matrix.toolchain }}
- name: Hide duplicated warnings from lint job
run: echo "::remove-matcher owner=clippy::"
- name: Build doc tests
run: cargo 3ds test --doc --verbose
# TODO: it would be nice to actually build 3dsx for examples/tests, etc. # TODO: it would be nice to actually build 3dsx for examples/tests, etc.
# and run it somehow, but exactly how remains to be seen. # and run it somehow, but exactly how remains to be seen.

57
ctru-rs/examples/graphics-bitmap.rs

@ -0,0 +1,57 @@
use ctru::prelude::*;
use ctru::services::gfx::Screen;
/// Ferris image taken from <https://rustacean.net> and scaled down to 320x240px.
/// To regenerate the data, you will need to install `imagemagick` and run this
/// command from the `examples` directory:
///
/// ```sh
/// magick assets/ferris.png -channel-fx "red<=>blue" -rotate 90 assets/ferris.rgb
/// ```
///
/// This creates an image appropriate for the default frame buffer format of
/// [`Bgr8`](ctru::services::gspgpu::FramebufferFormat::Bgr8)
/// and rotates the image 90° to account for the portrait mode screen.
static IMAGE: &[u8] = include_bytes!("assets/ferris.rgb");
fn main() {
ctru::use_panic_handler();
let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut());
println!("\x1b[21;16HPress Start to exit.");
let mut bottom_screen = gfx.bottom_screen.borrow_mut();
// We don't need double buffering in this example.
// In this way we can draw our image only once on screen.
bottom_screen.set_double_buffering(false);
// We assume the image is the correct size already, so we drop width + height.
let frame_buffer = bottom_screen.raw_framebuffer();
// Copy the image into the frame buffer
unsafe {
frame_buffer.ptr.copy_from(IMAGE.as_ptr(), IMAGE.len());
}
// 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::START) {
break;
}
// Flush and swap framebuffers
bottom_screen.flush_buffer();
bottom_screen.swap_buffers();
//Wait for VBlank
gfx.wait_for_vblank();
}
}

49
ctru-rs/examples/touch-screen.rs

@ -0,0 +1,49 @@
use ctru::prelude::*;
fn main() {
ctru::use_panic_handler();
let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller");
let console = Console::new(gfx.top_screen.borrow_mut());
// We'll hold the previous touch position for comparison.
let mut old_touch: (u16, u16) = (0, 0);
println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() {
hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
// Get X and Y coordinates of the touch point.
// The touch screen is 320x240.
let touch: (u16, u16) = hid.touch_position();
// We only want to print the position when it's different
// from what it was on the previous frame
if touch != old_touch {
// Special case for when the user lifts the stylus/finger from the screen.
// This is done to avoid some screen tearing.
if touch == (0, 0) {
console.clear();
// Print again because we just cleared the screen
println!("\x1b[29;16HPress Start to exit");
}
// Move the cursor back to the top of the screen and print the coordinates
print!("\x1b[1;1HTouch Screen position: {:#?}", touch);
}
// Save our current touch position for the next frame
old_touch = touch;
gfx.wait_for_vblank();
}
}

28
ctru-rs/src/applets/mii_selector.rs

@ -9,7 +9,7 @@ use std::ffi::CString;
/// Index of a Mii used to configure some parameters of the Mii Selector /// Index of a Mii used to configure some parameters of the Mii Selector
/// Can be either a single index, or _all_ Miis /// Can be either a single index, or _all_ Miis
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum MiiIndex { pub enum Index {
Index(u32), Index(u32),
All, All,
} }
@ -94,40 +94,40 @@ impl MiiSelector {
} }
/// Whitelist a guest Mii /// Whitelist a guest Mii
pub fn whitelist_guest_mii(&mut self, mii_index: MiiIndex) { pub fn whitelist_guest_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
MiiIndex::Index(i) => i, Index::Index(i) => i,
MiiIndex::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS, Index::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS,
}; };
unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), index) }
} }
/// Blacklist a guest Mii /// Blacklist a guest Mii
pub fn blacklist_guest_mii(&mut self, mii_index: MiiIndex) { pub fn blacklist_guest_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
MiiIndex::Index(i) => i, Index::Index(i) => i,
MiiIndex::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS, Index::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS,
}; };
unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) }
} }
/// Whitelist a user Mii /// Whitelist a user Mii
pub fn whitelist_user_mii(&mut self, mii_index: MiiIndex) { pub fn whitelist_user_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
MiiIndex::Index(i) => i, Index::Index(i) => i,
MiiIndex::All => ctru_sys::MIISELECTOR_USERMII_SLOTS, Index::All => ctru_sys::MIISELECTOR_USERMII_SLOTS,
}; };
unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) }
} }
/// Blacklist a user Mii /// Blacklist a user Mii
pub fn blacklist_user_mii(&mut self, mii_index: MiiIndex) { pub fn blacklist_user_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
MiiIndex::Index(i) => i, Index::Index(i) => i,
MiiIndex::All => ctru_sys::MIISELECTOR_USERMII_SLOTS, Index::All => ctru_sys::MIISELECTOR_USERMII_SLOTS,
}; };
unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) }
@ -185,7 +185,7 @@ impl From<ctru_sys::MiiSelectorReturn> for SelectionResult {
} }
} }
impl From<u32> for MiiIndex { impl From<u32> for Index {
fn from(v: u32) -> Self { fn from(v: u32) -> Self {
Self::Index(v) Self::Index(v)
} }

24
ctru-rs/src/services/cfgu.rs

@ -36,12 +36,12 @@ pub enum Language {
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)] #[repr(u32)]
pub enum SystemModel { pub enum SystemModel {
N3DS = ctru_sys::CFG_MODEL_3DS, Old3DS = ctru_sys::CFG_MODEL_3DS,
N3DSXL = ctru_sys::CFG_MODEL_3DSXL, Old3DSXL = ctru_sys::CFG_MODEL_3DSXL,
NewN3DS = ctru_sys::CFG_MODEL_N3DS, New3DS = ctru_sys::CFG_MODEL_N3DS,
N2DS = ctru_sys::CFG_MODEL_2DS, Old2DS = ctru_sys::CFG_MODEL_2DS,
NewN3DSXL = ctru_sys::CFG_MODEL_N3DSXL, New3DSXL = ctru_sys::CFG_MODEL_N3DSXL,
NewN2DSXL = ctru_sys::CFG_MODEL_N2DSXL, New2DSXL = ctru_sys::CFG_MODEL_N2DSXL,
} }
/// Represents the configuration service. No actions can be performed /// Represents the configuration service. No actions can be performed
@ -163,12 +163,12 @@ impl TryFrom<u8> for SystemModel {
fn try_from(value: u8) -> Result<Self, Self::Error> { fn try_from(value: u8) -> Result<Self, Self::Error> {
match value as u32 { match value as u32 {
ctru_sys::CFG_MODEL_3DS => Ok(SystemModel::N3DS), ctru_sys::CFG_MODEL_3DS => Ok(SystemModel::Old3DS),
ctru_sys::CFG_MODEL_3DSXL => Ok(SystemModel::N3DSXL), ctru_sys::CFG_MODEL_3DSXL => Ok(SystemModel::Old3DSXL),
ctru_sys::CFG_MODEL_N3DS => Ok(SystemModel::NewN3DS), ctru_sys::CFG_MODEL_N3DS => Ok(SystemModel::New3DS),
ctru_sys::CFG_MODEL_2DS => Ok(SystemModel::N2DS), ctru_sys::CFG_MODEL_2DS => Ok(SystemModel::Old2DS),
ctru_sys::CFG_MODEL_N3DSXL => Ok(SystemModel::NewN3DSXL), ctru_sys::CFG_MODEL_N3DSXL => Ok(SystemModel::New3DSXL),
ctru_sys::CFG_MODEL_N2DSXL => Ok(SystemModel::NewN2DSXL), ctru_sys::CFG_MODEL_N2DSXL => Ok(SystemModel::New2DSXL),
_ => Err(()), _ => Err(()),
} }
} }

57
ctru-rs/src/services/fs.rs

@ -99,7 +99,7 @@ pub struct Fs(());
/// ```no_run /// ```no_run
/// use ctru::services::fs::Fs; /// use ctru::services::fs::Fs;
/// ///
/// let fs = Fs::new().unwrap(); /// let mut fs = Fs::new().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap(); /// let sdmc_archive = fs.sdmc().unwrap();
/// ``` /// ```
pub struct Archive { pub struct Archive {
@ -119,47 +119,62 @@ pub struct Archive {
/// Create a new file and write bytes to it: /// Create a new file and write bytes to it:
/// ///
/// ```no_run /// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use std::io::prelude::*; /// use std::io::prelude::*;
/// use ctru::services::fs::{Fs, File}; /// use ctru::services::fs::{Fs, File};
/// ///
/// let fs = Fs::new()?; /// let mut fs = Fs::new()?;
/// let sdmc = fs.sdmc()?; /// let mut sdmc = fs.sdmc()?;
/// /// #
/// let mut file = File::create(&sdmc, "/foo.txt")?; /// # Ok(())
/// file.write_all(b"Hello, world!")?; /// # }
/// ``` /// ```
/// ///
/// Read the contents of a file into a `String`:: /// Read the contents of a file into a `String`::
/// ///
/// ```no_run /// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use std::io::prelude::*; /// use std::io::prelude::*;
/// use ctru::services::fs::{Fs, File}; /// use ctru::services::fs::{Fs, File};
/// ///
/// let fs = Fs::new()?; /// let mut fs = Fs::new()?;
/// let sdmc = fs.sdmc()?; /// let mut sdmc = fs.sdmc()?;
/// ///
/// let mut file = File::open(&sdmc, "/foo.txt")?; /// let mut file = File::open(&sdmc, "/foo.txt")?;
/// let mut contents = String::new(); /// let mut contents = String::new();
/// file.read_to_string(&mut contents)?; /// file.read_to_string(&mut contents)?;
/// assert_eq!(contents, "Hello, world!"); /// assert_eq!(contents, "Hello, world!");
/// #
/// # Ok(())
/// # }
/// ``` /// ```
/// ///
/// It can be more efficient to read the contents of a file with a buffered /// It can be more efficient to read the contents of a file with a buffered
/// `Read`er. This can be accomplished with `BufReader<R>`: /// `Read`er. This can be accomplished with `BufReader<R>`:
/// ///
/// ```no_run /// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use std::io::BufReader; /// use std::io::BufReader;
/// use std::io::prelude::*; /// use std::io::prelude::*;
/// use ctru::services::fs::{Fs, File}; /// use ctru::services::fs::{Fs, File};
/// ///
/// let fs = Fs::new()?; /// let mut fs = Fs::new()?;
/// let sdmc = fs.sdmc()?; /// let mut sdmc = fs.sdmc()?;
/// ///
/// let file = File::open(&sdmc, "/foo.txt")?; /// let file = File::open(&sdmc, "/foo.txt")?;
/// let mut buf_reader = BufReader::new(file); /// let mut buf_reader = BufReader::new(file);
/// let mut contents = String::new(); /// let mut contents = String::new();
/// buf_reader.read_to_string(&mut contents)?; /// buf_reader.read_to_string(&mut contents)?;
/// assert_eq!(contents, "Hello, world!"); /// assert_eq!(contents, "Hello, world!");
/// #
/// # Ok(())
/// # }
/// ``` /// ```
pub struct File { pub struct File {
handle: u32, handle: u32,
@ -205,13 +220,13 @@ pub struct Metadata {
/// ```no_run /// ```no_run
/// use ctru::services::fs::{Fs, OpenOptions}; /// use ctru::services::fs::{Fs, OpenOptions};
/// ///
<<<<<<< HEAD
/// let fs = Fs::new().unwrap(); /// let fs = Fs::new().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap(); /// let sdmc_archive = fs.sdmc().unwrap();
/// let file = OpenOptions::new() =======
/// .read(true) /// let mut fs = Fs::new().unwrap();
/// .archive(&sdmc_archive) /// .archive(&sdmc_archive)
/// .open("foo.txt") /// .open("foo.txt")
/// .unwrap();
/// ``` /// ```
/// ///
/// Opening a file for both reading and writing, as well as creating it if it /// Opening a file for both reading and writing, as well as creating it if it
@ -220,8 +235,13 @@ pub struct Metadata {
/// ```no_run /// ```no_run
/// use ctru::services::fs::{Fs, OpenOptions}; /// use ctru::services::fs::{Fs, OpenOptions};
/// ///
<<<<<<< HEAD
/// let fs = Fs::new().unwrap(); /// let fs = Fs::new().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap(); /// let sdmc_archive = fs.sdmc().unwrap();
=======
/// let mut fs = Fs::new().unwrap();
/// let mut sdmc_archive = fs.sdmc().unwrap();
>>>>>>> improve/api
/// let file = OpenOptions::new() /// let file = OpenOptions::new()
/// .read(true) /// .read(true)
/// .write(true) /// .write(true)
@ -347,8 +367,13 @@ impl File {
/// ```no_run /// ```no_run
/// use ctru::services::fs::{Fs, File}; /// use ctru::services::fs::{Fs, File};
/// ///
<<<<<<< HEAD
/// let fs = Fs::new().unwrap(); /// let fs = Fs::new().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap(); /// let sdmc_archive = fs.sdmc().unwrap();
=======
/// let mut fs = Fs::new().unwrap();
/// let mut sdmc_archive = fs.sdmc().unwrap();
>>>>>>> improve/api
/// let mut f = File::open(&sdmc_archive, "/foo.txt").unwrap(); /// let mut f = File::open(&sdmc_archive, "/foo.txt").unwrap();
/// ``` /// ```
pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> IoResult<File> { pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> IoResult<File> {
@ -376,9 +401,15 @@ impl File {
/// ```no_run /// ```no_run
/// use ctru::services::fs::{Fs, File}; /// use ctru::services::fs::{Fs, File};
/// ///
<<<<<<< HEAD
/// let fs = Fs::new().unwrap(); /// let fs = Fs::new().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap(); /// let sdmc_archive = fs.sdmc().unwrap();
/// let mut f = File::create(&sdmc_archive, "/foo.txt").unwrap(); /// let mut f = File::create(&sdmc_archive, "/foo.txt").unwrap();
=======
/// let mut fs = Fs::new().unwrap();
/// let mut sdmc_archive = fs.sdmc().unwrap();
/// let mut f = File::create(&mut sdmc_archive, "/foo.txt").unwrap();
>>>>>>> improve/api
/// ``` /// ```
pub fn create<P: AsRef<Path>>(arch: &mut Archive, path: P) -> IoResult<File> { pub fn create<P: AsRef<Path>>(arch: &mut Archive, path: P) -> IoResult<File> {
OpenOptions::new() OpenOptions::new()

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

@ -46,14 +46,6 @@ bitflags::bitflags! {
/// This service requires no special permissions to use. /// This service requires no special permissions to use.
pub struct Hid(()); pub struct Hid(());
/// Represents user input to the touchscreen.
#[derive(Debug, Clone, Copy)]
pub struct TouchPosition(ctru_sys::touchPosition);
/// Represents the current position of the 3DS circle pad.
#[derive(Debug, Clone, Copy)]
pub struct CirclePosition(ctru_sys::circlePosition);
/// Initializes the HID service. /// Initializes the HID service.
/// ///
/// # Errors /// # Errors
@ -102,47 +94,33 @@ impl Hid {
KeyPad::from_bits_truncate(keys) KeyPad::from_bits_truncate(keys)
} }
} }
}
impl Default for TouchPosition {
fn default() -> Self {
TouchPosition(ctru_sys::touchPosition { px: 0, py: 0 })
}
}
impl TouchPosition { /// Returns the current touch position in pixels (x, y).
/// Create a new TouchPosition instance. ///
pub fn new() -> Self { /// # Notes
Self::default() ///
} /// (0, 0) represents the top left corner of the screen.
pub fn touch_position(&mut self) -> (u16, u16) {
let mut res = ctru_sys::touchPosition { px: 0, py: 0 };
/// Returns the current touch position in pixels.
pub fn get(&mut self) -> (u16, u16) {
unsafe { unsafe {
ctru_sys::hidTouchRead(&mut self.0); ctru_sys::hidTouchRead(&mut res);
}
(self.0.px, self.0.py)
}
}
impl Default for CirclePosition {
fn default() -> Self {
CirclePosition(ctru_sys::circlePosition { dx: 0, dy: 0 })
} }
(res.px, res.py)
} }
impl CirclePosition { /// Returns the current circle pad position in relative (x, y).
/// Create a new CirclePosition instance. ///
pub fn new() -> Self { /// # Notes
Self::default() ///
} /// (0, 0) represents the center of the circle pad.
pub fn circlepad_position(&mut self) -> (i16, i16) {
let mut res = ctru_sys::circlePosition { dx: 0, dy: 0 };
/// Returns the current circle pad position in (x, y) form.
pub fn get(&mut self) -> (i16, i16) {
unsafe { unsafe {
ctru_sys::hidCircleRead(&mut self.0); ctru_sys::hidCircleRead(&mut res);
} }
(self.0.dx, self.0.dy) (res.dx, res.dy)
} }
} }

Loading…
Cancel
Save