├── src ├── hipc.rs ├── sys.rs ├── twili │ ├── sys.rs │ └── mod.rs ├── applet │ ├── mod.rs │ ├── swkbd.rs │ └── libapplet.rs ├── macros │ ├── mod.rs │ ├── hid.rs │ ├── result.rs │ └── service.rs ├── os │ ├── mod.rs │ ├── kernel.rs │ └── version.rs ├── console.rs ├── sm.rs ├── util.rs ├── lib.rs ├── usbcomms.rs └── hid.rs ├── .gitmodules ├── .gitignore ├── Xargo.toml ├── Cargo.toml ├── README.md ├── Dockerfile ├── LICENSE └── bindgen ├── twili.h ├── twili.rs └── libnx.h /src/hipc.rs: -------------------------------------------------------------------------------- 1 | use sys; 2 | -------------------------------------------------------------------------------- /src/sys.rs: -------------------------------------------------------------------------------- 1 | include!("../bindgen/libnx.rs"); 2 | -------------------------------------------------------------------------------- /src/twili/sys.rs: -------------------------------------------------------------------------------- 1 | include!("../../bindgen/twili.rs"); 2 | -------------------------------------------------------------------------------- /src/applet/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod libapplet; 2 | pub mod swkbd; 3 | -------------------------------------------------------------------------------- /src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | pub mod hid; 4 | pub mod result; 5 | pub mod service; 6 | -------------------------------------------------------------------------------- /src/os/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kernel; 2 | pub mod version; 3 | 4 | pub type Result = std::result::Result; 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "twili-libnx"] 2 | path = twili-libnx 3 | url = https://github.com/misson20000/twili-libnx.git 4 | -------------------------------------------------------------------------------- /src/twili/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_camel_case_types)] 2 | #[allow(non_upper_case_globals)] 3 | #[allow(non_snake_case)] 4 | #[allow(dead_code)] 5 | #[allow(clippy::all)] 6 | #[allow(clippy::pedantic)] 7 | pub mod sys; 8 | 9 | handle!(0 in sys::twiliInitialize(), sys::twiliExit(), {}); 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | use sys; 2 | 3 | handle!(_ in sys::consoleInit(std::ptr::null_mut()), sys::consoleExit(std::ptr::null_mut()), { 4 | pub fn clear(&self) { 5 | unsafe { 6 | sys::consoleClear(); 7 | } 8 | } 9 | 10 | pub fn flush(&self) { 11 | unsafe { 12 | sys::consoleUpdate(std::ptr::null_mut()); 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /src/sm.rs: -------------------------------------------------------------------------------- 1 | use os; 2 | use sys; 3 | 4 | handle!(0 in sys::smInitialize(), sys::smExit(), { 5 | pub fn get_service(&self, name: &str) -> os::Result { 6 | unsafe { 7 | let mut srv: sys::Service = std::mem::zeroed(); 8 | let rc = sys::smGetService(&mut srv, name.as_ptr()); 9 | result_final!(rc, srv) 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /src/os/kernel.rs: -------------------------------------------------------------------------------- 1 | use sys; 2 | 3 | pub fn get_current_process_handle() -> u32 { 4 | 0xffff_8001 5 | } 6 | 7 | pub fn is_nso() -> bool { 8 | unsafe { sys::envIsNso() } 9 | } 10 | 11 | pub fn is_nro() -> bool { 12 | !is_nso() 13 | } 14 | 15 | pub fn nro_exec(path: &str) { 16 | unsafe { 17 | sys::envSetNextLoad(path.as_ptr(), path.as_ptr()); 18 | std::process::exit(0); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Xargo.toml: -------------------------------------------------------------------------------- 1 | [dependencies.core] 2 | stage = 0 3 | 4 | [dependencies.alloc] 5 | stage = 1 6 | 7 | [dependencies.libc] 8 | stage = 2 9 | default-features = false 10 | features = ["align", "rustc-dep-of-std"] 11 | 12 | [dependencies.std] 13 | git = "https://github.com/rusty-horizon/horizon-nx-std-squashed" 14 | branch = "switch-squashed" 15 | default-features = false 16 | features = [ 17 | "panic-unwind", 18 | #"backtrace", 19 | ] 20 | stage = 3 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nx" 3 | version = "0.1.0" 4 | authors = ["XorTroll", "ischeinkman ", "Switchbrew"] 5 | links = "nx" 6 | 7 | [lib] 8 | crate_type = ["rlib"] 9 | 10 | [dependencies] 11 | cfg-if = "0.1" 12 | 13 | [build-dependencies] 14 | bindgen = { version = "0.37.4", optional = true } 15 | cc = { version = "1.0.37", optional = true } 16 | cfg-if = "0.1" 17 | 18 | [features] 19 | default = [] 20 | twili = ["cc"] 21 | sysroot = [] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libnx - Rust edition 2 | 3 | This mostly consists on [libnx](https://github.com/swicthbrew/libnx) raw bindings plus some high level implementations within modules. 4 | 5 | As soon as the project gets bigger and more stable, more information and examples will be added here. 6 | 7 | ## Credits 8 | 9 | - [ischeinkman](https://github.com/ischeinkman)'s [libnx-rs](https://github.com/ischeinkman/libnx-rs) as the base of this project, considering that this is a slightly different but extended version of his bindings. 10 | 11 | - libnx and SwitchBrew, of course. -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::panic; 2 | use std::process::exit; 3 | use std::thread; 4 | 5 | /// Makes panics print to stdout and exit with code 0, avoiding causing a reboot. 6 | pub fn no_crash_panic() { 7 | let old_hook = panic::take_hook(); 8 | panic::set_hook(Box::new(move |info| { 9 | old_hook(info); 10 | let thread = thread::current(); 11 | let name = thread.name().unwrap_or(""); 12 | println!("thread '{}' {}", name, info); 13 | exit(0); 14 | })); 15 | } 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM devkitpro/devkita64 2 | ENV PATH=$DEVKITPRO/devkitA64/bin:$PATH 3 | 4 | # Install GCC for the CC link 5 | RUN sudo apt-get update 6 | RUN sudo apt-get install -y build-essential 7 | 8 | # Install Rust 9 | RUN curl https://sh.rustup.rs -sSf > rust-init.rs 10 | RUN chmod +x rust-init.rs 11 | RUN ./rust-init.rs -y --default-toolchain nightly-2019-01-19 12 | RUN rm rust-init.rs 13 | ENV PATH=/root/.cargo/bin:$PATH 14 | RUN rustup component add rust-src 15 | RUN cargo install xargo 16 | 17 | # Mount the work directory 18 | WORKDIR workdir 19 | VOLUME workdir 20 | -------------------------------------------------------------------------------- /src/macros/hid.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | #[macro_export] 4 | macro_rules! input_any 5 | { 6 | ($ipt:expr, $($id:expr),*) => 7 | {{ 8 | let mut hmatch = false; 9 | $( 10 | if ($ipt & ($id as u64)) != 0 11 | { 12 | hmatch = true; 13 | } 14 | )* 15 | hmatch 16 | }}; 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! input_all 21 | { 22 | ($ipt:expr, $($id:expr),*) => 23 | {{ 24 | let mut hmatch = true; 25 | $( 26 | if ($ipt & ($id as u64)) == 0 27 | { 28 | hmatch = false; 29 | } 30 | )* 31 | hmatch 32 | }}; 33 | } 34 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "sysroot", no_std)] 2 | #![cfg_attr(not(feature = "sysroot"), feature(asm))] 3 | #![macro_use] 4 | 5 | #[cfg(not(feature = "sysroot"))] 6 | extern crate core; 7 | 8 | extern crate cfg_if; 9 | 10 | use cfg_if::cfg_if; 11 | 12 | #[allow(non_camel_case_types)] 13 | #[allow(non_upper_case_globals)] 14 | #[allow(non_snake_case)] 15 | #[allow(dead_code)] 16 | #[allow(clippy::all)] 17 | #[allow(clippy::pedantic)] 18 | pub mod sys; 19 | 20 | cfg_if! { 21 | if #[cfg(not(feature = "sysroot"))] { 22 | pub mod macros; 23 | 24 | pub mod sm; 25 | 26 | pub mod console; 27 | 28 | pub mod hid; 29 | 30 | pub mod applet; 31 | 32 | pub mod os; 33 | 34 | pub mod usbcomms; 35 | 36 | mod util; 37 | pub use util::*; 38 | } 39 | } 40 | 41 | #[cfg(feature = "twili")] 42 | pub mod twili; 43 | -------------------------------------------------------------------------------- /src/macros/result.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | #[macro_export] 4 | macro_rules! result_make { 5 | ($mdl:expr, $desc:expr) => {{ 6 | (($mdl & 0x1ff) | ($desc & 0x1fff) << 9) as u32 7 | }}; 8 | } 9 | 10 | #[macro_export] 11 | macro_rules! result_assert { 12 | ($rc:expr) => {{ 13 | if $rc != 0 { 14 | return Err($rc); 15 | } 16 | }}; 17 | 18 | ($rc:expr, $cb:expr) => {{ 19 | if $rc != 0 { 20 | $cb(); 21 | return Err($rc); 22 | } 23 | }}; 24 | } 25 | 26 | #[macro_export] 27 | macro_rules! result_final { 28 | ($rc:expr) => {{ 29 | return match $rc { 30 | 0 => Ok(()), 31 | _ => Err($rc), 32 | }; 33 | }}; 34 | 35 | ($rc:expr, $val:expr) => {{ 36 | return match $rc { 37 | 0 => Ok($val), 38 | _ => Err($rc), 39 | }; 40 | }}; 41 | } 42 | -------------------------------------------------------------------------------- /src/os/version.rs: -------------------------------------------------------------------------------- 1 | use sys; 2 | 3 | pub struct Version { 4 | pub major: u8, 5 | pub minor: u8, 6 | pub micro: u8, 7 | } 8 | 9 | impl std::fmt::Display for Version { 10 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 11 | write!(f, "{}.{}.{}", self.major, self.minor, self.micro) 12 | } 13 | } 14 | 15 | #[allow(clippy::cast_possible_truncation)] 16 | fn hosv_to_version(v: u32) -> Version { 17 | let tmpmajor: u8 = ((v >> 16) & 0xff) as u8; 18 | let tmpminor: u8 = ((v >> 8) & 0xff) as u8; 19 | let tmpmicro: u8 = (v & 0xff) as u8; 20 | Version { 21 | major: tmpmajor, 22 | minor: tmpminor, 23 | micro: tmpmicro, 24 | } 25 | } 26 | 27 | pub fn get() -> Version { 28 | unsafe { 29 | let hosv: u32 = sys::hosversionGet(); 30 | hosv_to_version(hosv) 31 | } 32 | } 33 | 34 | #[deprecated(note = "Please use version::get() instead.")] 35 | #[allow(clippy::module_name_repetitions)] 36 | pub fn get_version() -> Version { 37 | get() 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ilan Scheinkman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bindgen/twili.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 misson20000 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any purpose 5 | with or without fee is hereby granted, provided that the above copyright notice 6 | and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 12 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 13 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 14 | THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | Result twiliWriteNamedPipe(Service *pipe, const char *ptr, size_t len); 23 | Result twiliCreateNamedOutputPipe(Service *srv_out, const char* name, size_t len); 24 | Result twiliInitialize(void); 25 | void twiliExit(void); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | -------------------------------------------------------------------------------- /src/macros/service.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | #[macro_export] 4 | macro_rules! handle { 5 | (_ in $init:expr, $exit:expr, {$($impl:tt)*}) => { 6 | #[derive(Debug)] 7 | pub struct Handle(()); 8 | 9 | static INITIALIZED: ::std::sync::atomic::AtomicBool = 10 | ::std::sync::atomic::AtomicBool::new(false); 11 | 12 | impl Drop for Handle { 13 | fn drop(&mut self) { 14 | unsafe { $exit; }; 15 | } 16 | } 17 | 18 | impl Handle { 19 | pub fn new() -> Option { 20 | if !INITIALIZED.swap(true, ::std::sync::atomic::Ordering::SeqCst) { 21 | unsafe { $init; }; 22 | 23 | Some(Handle(())) 24 | } else { 25 | None 26 | } 27 | } 28 | 29 | $($impl)* 30 | } 31 | }; 32 | 33 | ($ok:pat in $init:expr, $exit:expr, {$($impl:tt)*}) => { 34 | #[derive(Debug)] 35 | pub struct Handle(()); 36 | 37 | static INITIALIZED: ::std::sync::atomic::AtomicBool = 38 | ::std::sync::atomic::AtomicBool::new(false); 39 | 40 | impl Drop for Handle { 41 | fn drop(&mut self) { 42 | unsafe { $exit; }; 43 | } 44 | } 45 | 46 | impl Handle { 47 | pub fn new() -> Option> { 48 | if !INITIALIZED.swap(true, ::std::sync::atomic::Ordering::SeqCst) { 49 | let res = unsafe { $init }; 50 | 51 | match res as u32 { 52 | $ok => Some(Ok(Handle(()))), 53 | err => Some(Err(err as u32)), 54 | } 55 | } else { 56 | None 57 | } 58 | } 59 | 60 | $($impl)* 61 | } 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /src/applet/swkbd.rs: -------------------------------------------------------------------------------- 1 | use os; 2 | use sys; 3 | 4 | pub struct Keyboard { 5 | kbd: sys::SwkbdConfig, 6 | } 7 | 8 | pub enum KeyboardPreset { 9 | Default, 10 | Password, 11 | UserName, 12 | DownloadCode, 13 | } 14 | 15 | impl Keyboard { 16 | pub fn new() -> os::Result { 17 | unsafe { 18 | let mut kbd: sys::SwkbdConfig = std::mem::zeroed(); 19 | let rc = sys::swkbdCreate(&mut kbd, 0); 20 | result_final!(rc, Self { kbd: kbd }) 21 | } 22 | } 23 | 24 | pub fn set_preset(&mut self, preset: &KeyboardPreset) { 25 | unsafe { 26 | match preset { 27 | KeyboardPreset::Default => sys::swkbdConfigMakePresetDefault(&mut self.kbd), 28 | KeyboardPreset::Password => sys::swkbdConfigMakePresetPassword(&mut self.kbd), 29 | KeyboardPreset::UserName => sys::swkbdConfigMakePresetUserName(&mut self.kbd), 30 | KeyboardPreset::DownloadCode => { 31 | sys::swkbdConfigMakePresetDownloadCode(&mut self.kbd) 32 | } 33 | }; 34 | } 35 | } 36 | 37 | pub fn set_ok_button_text(&mut self, text: &str) { 38 | unsafe { 39 | sys::swkbdConfigSetOkButtonText(&mut self.kbd, text.as_ptr()); 40 | } 41 | } 42 | 43 | pub fn show(&mut self) -> os::Result { 44 | unsafe { 45 | let mut out_buf: [u8; 500] = [0; 500]; 46 | let out_ptr = out_buf.as_mut_ptr(); 47 | let rc = sys::swkbdShow(&mut self.kbd, out_ptr, 500); 48 | result_final!( 49 | rc, 50 | String::from_utf8_lossy(std::slice::from_raw_parts(out_ptr, 500)).to_string() 51 | ) 52 | } 53 | } 54 | } 55 | 56 | impl Drop for Keyboard { 57 | fn drop(&mut self) { 58 | unsafe { 59 | sys::swkbdClose(&mut self.kbd); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/applet/libapplet.rs: -------------------------------------------------------------------------------- 1 | use os; 2 | use sys; 3 | 4 | pub struct LibraryApplet { 5 | holder: sys::AppletHolder, 6 | } 7 | 8 | impl LibraryApplet { 9 | pub fn new(id: sys::AppletId, mode: sys::LibAppletMode, version: u32) -> os::Result { 10 | unsafe { 11 | let mut aph: sys::AppletHolder = std::mem::zeroed(); 12 | let mut rc = sys::appletCreateLibraryApplet(&mut aph, id, mode); 13 | result_assert!(rc, { 14 | println!("Exit"); 15 | }); 16 | let mut largs: sys::LibAppletArgs = std::mem::zeroed(); 17 | sys::libappletArgsCreate(&mut largs, version); 18 | rc = sys::libappletArgsPush(&mut largs, &mut aph); 19 | result_final!(rc, Self { holder: aph }) 20 | } 21 | } 22 | 23 | pub fn push_data(&mut self, data: *const u8, size: usize) -> os::Result<()> { 24 | unsafe { 25 | let rc = 26 | sys::libappletPushInData(&mut self.holder, data as *const std::ffi::c_void, size); 27 | result_final!(rc) 28 | } 29 | } 30 | 31 | pub fn show(&mut self) -> os::Result<()> { 32 | unsafe { 33 | let rc = sys::appletHolderStart(&mut self.holder); 34 | result_final!(rc) 35 | } 36 | } 37 | 38 | pub fn show_and_wait(&mut self) -> os::Result<()> { 39 | unsafe { 40 | let rc = sys::appletHolderStart(&mut self.holder); 41 | result_assert!(rc); 42 | while sys::appletHolderWaitInteractiveOut(&mut self.holder) {} 43 | sys::appletHolderJoin(&mut self.holder); 44 | result_final!(rc) 45 | } 46 | } 47 | 48 | pub fn pop_data(&mut self, out: *mut u8, size: usize) -> os::Result { 49 | unsafe { 50 | let mut out_size: usize = 0; 51 | let rc = sys::libappletPopOutData( 52 | &mut self.holder, 53 | out as *mut std::ffi::c_void, 54 | size, 55 | &mut out_size, 56 | ); 57 | result_final!(rc, out_size) 58 | } 59 | } 60 | } 61 | 62 | impl Drop for LibraryApplet { 63 | fn drop(&mut self) { 64 | unsafe { 65 | sys::appletHolderClose(&mut self.holder); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/usbcomms.rs: -------------------------------------------------------------------------------- 1 | use sys; 2 | 3 | pub use sys::UsbCommsInterfaceInfo; 4 | 5 | use std::sync::atomic::{AtomicBool, Ordering}; 6 | 7 | use os; 8 | #[derive(Debug)] 9 | pub struct Handle(()); 10 | 11 | static INITIALIZED: AtomicBool = AtomicBool::new(false); 12 | 13 | impl Drop for Handle { 14 | fn drop(&mut self) { 15 | if INITIALIZED.swap(false, Ordering::SeqCst) { 16 | unsafe { sys::usbCommsExit() }; 17 | } 18 | } 19 | } 20 | 21 | impl Handle { 22 | pub fn new() -> Option> { 23 | if !INITIALIZED.swap(true, Ordering::SeqCst) { 24 | let res = unsafe { sys::usbCommsInitialize() }; 25 | match res { 26 | 0 => Some(Ok(Handle(()))), 27 | err => Some(Err(err)), 28 | } 29 | } else { 30 | None 31 | } 32 | } 33 | 34 | pub fn new_ex(infos: &[UsbCommsInterfaceInfo]) -> Option> { 35 | let num_interfaces = infos.len() as u32; 36 | let infos_ptr = infos as *const _ as *const _; 37 | if !INITIALIZED.swap(true, Ordering::SeqCst) { 38 | let res = unsafe { sys::usbCommsInitializeEx(num_interfaces, infos_ptr) }; 39 | match res { 40 | 0 => Some(Ok(Handle(()))), 41 | err => Some(Err(err)), 42 | } 43 | } else { 44 | None 45 | } 46 | } 47 | 48 | pub fn read_ex(&mut self, buffer: &mut [u8], interface: u32) -> usize { 49 | let buff_ptr = buffer as *mut _ as *mut _; 50 | let size = buffer.len(); 51 | unsafe { sys::usbCommsReadEx(buff_ptr, size, interface) } 52 | } 53 | 54 | pub fn read(&mut self, buffer: &mut [u8]) -> usize { 55 | let buff_ptr = buffer as *mut _ as *mut _; 56 | let size = buffer.len(); 57 | unsafe { sys::usbCommsRead(buff_ptr, size) } 58 | } 59 | 60 | pub fn write_ex(&mut self, buffer: &[u8], interface: u32) -> usize { 61 | let buff_ptr = buffer as *const _ as *const _; 62 | let size = buffer.len(); 63 | unsafe { sys::usbCommsWriteEx(buff_ptr, size, interface) } 64 | } 65 | 66 | pub fn write(&mut self, buffer: &[u8]) -> usize { 67 | let buff_ptr = buffer as *const _ as *const _; 68 | let size = buffer.len(); 69 | unsafe { sys::usbCommsWrite(buff_ptr, size) } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /bindgen/twili.rs: -------------------------------------------------------------------------------- 1 | mod ctypes { 2 | pub type c_void = core::ffi::c_void; 3 | pub type c_char = u8; 4 | pub type c_int = i32; 5 | pub type c_long = i64; 6 | pub type c_longlong = i64; 7 | pub type c_schar = i8; 8 | pub type c_short = i16; 9 | pub type c_uchar = u8; 10 | pub type c_uint = u32; 11 | pub type c_ulong = u64; 12 | pub type c_ulonglong = u64; 13 | pub type c_ushort = u16; 14 | pub type size_t = u64; 15 | pub type ssize_t = i64; 16 | pub type c_float = f32; 17 | pub type c_double = f64; 18 | }/* automatically generated by rust-bindgen */ 19 | 20 | pub type __uint32_t = ctypes :: c_uint ; pub type Handle = u32 ; pub type Result = u32 ; 21 | /// < Uninitialized service. 22 | pub const ServiceType_ServiceType_Uninitialized : ServiceType = 0 ; 23 | /// < Normal service. 24 | pub const ServiceType_ServiceType_Normal : ServiceType = 1 ; 25 | /// < Domain. 26 | pub const ServiceType_ServiceType_Domain : ServiceType = 2 ; 27 | /// < Domain subservice; 28 | pub const ServiceType_ServiceType_DomainSubservice : ServiceType = 3 ; 29 | /// < Service overriden in the homebrew environment. 30 | pub const ServiceType_ServiceType_Override : ServiceType = 4 ; 31 | /// Service type. 32 | pub type ServiceType = u32 ; 33 | /// Service object structure. 34 | # [ repr ( C ) ] pub struct Service { pub handle : Handle , pub object_id : u32 , pub type_ : ServiceType , } # [ test ] fn bindgen_test_layout_Service ( ) { assert_eq ! ( :: core :: mem :: size_of :: < Service > ( ) , 12usize , concat ! ( "Size of: " , stringify ! ( Service ) ) ) ; assert_eq ! ( :: core :: mem :: align_of :: < Service > ( ) , 4usize , concat ! ( "Alignment of " , stringify ! ( Service ) ) ) ; assert_eq ! ( unsafe { & ( * ( :: core :: ptr :: null :: < Service > ( ) ) ) . handle as * const _ as usize } , 0usize , concat ! ( "Offset of field: " , stringify ! ( Service ) , "::" , stringify ! ( handle ) ) ) ; assert_eq ! ( unsafe { & ( * ( :: core :: ptr :: null :: < Service > ( ) ) ) . object_id as * const _ as usize } , 4usize , concat ! ( "Offset of field: " , stringify ! ( Service ) , "::" , stringify ! ( object_id ) ) ) ; assert_eq ! ( unsafe { & ( * ( :: core :: ptr :: null :: < Service > ( ) ) ) . type_ as * const _ as usize } , 8usize , concat ! ( "Offset of field: " , stringify ! ( Service ) , "::" , stringify ! ( type_ ) ) ) ; } extern "C" { pub fn twiliWriteNamedPipe ( pipe : * mut Service , ptr : * const ctypes :: c_char , len : usize ) -> Result ; } extern "C" { pub fn twiliCreateNamedOutputPipe ( srv_out : * mut Service , name : * const ctypes :: c_char , len : usize ) -> Result ; } extern "C" { pub fn twiliInitialize ( ) -> Result ; } extern "C" { pub fn twiliExit ( ) ; } -------------------------------------------------------------------------------- /src/hid.rs: -------------------------------------------------------------------------------- 1 | use sys; 2 | 3 | #[derive(Clone, Copy)] 4 | pub enum Controller { 5 | Invalid, 6 | Handheld, 7 | Player(u8), 8 | } 9 | 10 | pub enum Key { 11 | None, 12 | A = 1, 13 | B = 2, 14 | X = 4, 15 | Y = 8, 16 | LStick = 16, 17 | RStick = 32, 18 | L = 64, 19 | R = 128, 20 | ZL = 256, 21 | ZR = 512, 22 | Plus = 1024, 23 | Minus = 2048, 24 | DPadRight = 16384, 25 | DPadUp = 8192, 26 | DPadDown = 32768, 27 | DPadLeft = 4096, 28 | } 29 | 30 | pub enum JoyConHoldMode { 31 | Default, 32 | Horizontal, 33 | } 34 | 35 | pub fn ctrlid_to_controller(id: sys::HidControllerID) -> Controller { 36 | match id { 37 | sys::HidControllerID_CONTROLLER_PLAYER_1 => Controller::Player(1), 38 | sys::HidControllerID_CONTROLLER_PLAYER_2 => Controller::Player(2), 39 | sys::HidControllerID_CONTROLLER_PLAYER_3 => Controller::Player(3), 40 | sys::HidControllerID_CONTROLLER_PLAYER_4 => Controller::Player(4), 41 | sys::HidControllerID_CONTROLLER_PLAYER_5 => Controller::Player(5), 42 | sys::HidControllerID_CONTROLLER_PLAYER_6 => Controller::Player(6), 43 | sys::HidControllerID_CONTROLLER_PLAYER_7 => Controller::Player(7), 44 | sys::HidControllerID_CONTROLLER_PLAYER_8 => Controller::Player(8), 45 | sys::HidControllerID_CONTROLLER_HANDHELD => Controller::Handheld, 46 | _ => Controller::Invalid, 47 | } 48 | } 49 | 50 | pub fn controller_to_ctrlid(id: Controller) -> sys::HidControllerID { 51 | match id { 52 | Controller::Player(1) => sys::HidControllerID_CONTROLLER_PLAYER_1, 53 | Controller::Player(2) => sys::HidControllerID_CONTROLLER_PLAYER_2, 54 | Controller::Player(3) => sys::HidControllerID_CONTROLLER_PLAYER_3, 55 | Controller::Player(4) => sys::HidControllerID_CONTROLLER_PLAYER_4, 56 | Controller::Player(5) => sys::HidControllerID_CONTROLLER_PLAYER_5, 57 | Controller::Player(6) => sys::HidControllerID_CONTROLLER_PLAYER_6, 58 | Controller::Player(7) => sys::HidControllerID_CONTROLLER_PLAYER_7, 59 | Controller::Player(8) => sys::HidControllerID_CONTROLLER_PLAYER_8, 60 | Controller::Handheld => sys::HidControllerID_CONTROLLER_HANDHELD, 61 | _ => sys::HidControllerID_CONTROLLER_UNKNOWN, 62 | } 63 | } 64 | 65 | pub fn is_controller_connected(ctrl: Controller) -> bool { 66 | unsafe { sys::hidIsControllerConnected(controller_to_ctrlid(ctrl)) } 67 | } 68 | 69 | pub fn flush() { 70 | unsafe { 71 | sys::hidScanInput(); 72 | } 73 | } 74 | 75 | pub fn input_down(ctrl: Controller) -> u64 { 76 | unsafe { 77 | flush(); 78 | sys::hidKeysDown(controller_to_ctrlid(ctrl)) 79 | } 80 | } 81 | 82 | pub fn input_up(ctrl: Controller) -> u64 { 83 | unsafe { 84 | flush(); 85 | sys::hidKeysUp(controller_to_ctrlid(ctrl)) 86 | } 87 | } 88 | 89 | pub fn input_held(ctrl: Controller) -> u64 { 90 | unsafe { 91 | flush(); 92 | sys::hidKeysHeld(controller_to_ctrlid(ctrl)) 93 | } 94 | } 95 | 96 | pub fn get_touch_count() -> u32 { 97 | unsafe { sys::hidTouchCount() } 98 | } 99 | 100 | pub fn get_touch_coords(index: u32) -> (u32, u32) { 101 | unsafe { 102 | flush(); 103 | let mut tch: sys::touchPosition = std::mem::zeroed(); 104 | sys::hidTouchRead(&mut tch, index); 105 | (tch.px, tch.py) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /bindgen/libnx.h: -------------------------------------------------------------------------------- 1 | 2 | // Using this header for bindgen to simplify binding process 3 | 4 | #pragma once 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | // Not direcly including switch.h since new CRC crypto dependencies break bindgen, so skipping CRC header 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | 98 | #include 99 | #include 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | 107 | #include 108 | 109 | #include 110 | #include 111 | #include 112 | #include 113 | #include 114 | 115 | #include 116 | #include 117 | #include 118 | 119 | #include 120 | 121 | #include 122 | #include 123 | #include 124 | #include 125 | #include 126 | 127 | #include 128 | #include 129 | #include 130 | #include 131 | #include 132 | 133 | #include 134 | #include 135 | #include 136 | 137 | #ifdef __cplusplus 138 | } 139 | #endif --------------------------------------------------------------------------------