diff --git a/ctru-rs/examples/output-3dslink.rs b/ctru-rs/examples/output-3dslink.rs index 36ae187..9c66c9b 100644 --- a/ctru-rs/examples/output-3dslink.rs +++ b/ctru-rs/examples/output-3dslink.rs @@ -24,8 +24,6 @@ fn main() { soc.redirect_to_3dslink(true, true) .expect("unable to redirect stdout/err to 3dslink server"); - print!("\x1b[2J\x1b[0;0H"); // Clear screen + move to 0,0 - println!("Hello 3dslink!"); eprintln!("Press Start on the device to disconnect and exit."); diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index 96ade26..047f811 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -1,4 +1,5 @@ use std::error; +use std::ffi::CStr; use std::fmt; use ctru_sys::result::{R_DESCRIPTION, R_LEVEL, R_MODULE, R_SUMMARY}; @@ -9,7 +10,27 @@ pub type Result = ::std::result::Result; #[non_exhaustive] pub enum Error { Os(ctru_sys::Result), + Libc(String), ServiceAlreadyActive, + OutputAlreadyRedirected, +} + +impl Error { + /// Create an [`Error`] out of the last set value in `errno`. This can be used + /// to get a human-readable error string from calls to `libc` functions. + pub(crate) fn from_errno() -> Self { + let error_str = unsafe { + let errno = ctru_sys::errno(); + let str_ptr = libc::strerror(errno); + + // Safety: strerror should always return a valid string, + // even if the error number is unknown + CStr::from_ptr(str_ptr) + }; + + // Copy out of the error string, since it may be changed by other libc calls later + Self::Libc(error_str.to_string_lossy().into()) + } } impl From for Error { @@ -20,8 +41,8 @@ impl From for Error { impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Os(err) => f + match self { + &Self::Os(err) => f .debug_struct("Error") .field("raw", &format_args!("{:#08X}", err)) .field("description", &R_DESCRIPTION(err)) @@ -29,7 +50,9 @@ impl fmt::Debug for Error { .field("summary", &R_SUMMARY(err)) .field("level", &R_LEVEL(err)) .finish(), - Error::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(), + Self::Libc(err) => f.debug_tuple("Libc").field(err).finish(), + Self::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(), + Self::OutputAlreadyRedirected => f.debug_tuple("OutputAlreadyRedirected").finish(), } } } @@ -39,9 +62,13 @@ impl fmt::Debug for Error { // https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/result.h impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Os(err) => write!(f, "libctru result code: 0x{:08X}", err), - Error::ServiceAlreadyActive => write!(f, "Service already active"), + match self { + &Self::Os(err) => write!(f, "libctru result code: 0x{:08X}", err), + Self::Libc(err) => write!(f, "{}", err), + Self::ServiceAlreadyActive => write!(f, "Service already active"), + Self::OutputAlreadyRedirected => { + write!(f, "output streams are already redirected to 3dslink") + } } } } diff --git a/ctru-rs/src/services/soc.rs b/ctru-rs/src/services/soc.rs index bcbdcc8..13d0dc9 100644 --- a/ctru-rs/src/services/soc.rs +++ b/ctru-rs/src/services/soc.rs @@ -4,6 +4,7 @@ use std::net::Ipv4Addr; use std::sync::Mutex; use crate::services::ServiceReference; +use crate::Error; /// Soc service. Initializing this service will enable the use of network sockets and utilities /// such as those found in `std::net`. The service will be closed when this struct is is dropped. @@ -74,15 +75,17 @@ impl Soc { /// output was already previously redirected. pub fn redirect_to_3dslink(&mut self, stdout: bool, stderr: bool) -> crate::Result<()> { if self.sock_3dslink >= 0 { - // TODO AlreadyRedirected or something - return Err(crate::Error::ServiceAlreadyActive); + return Err(Error::OutputAlreadyRedirected); } - let sock = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) }; - if sock < 0 { - Err(sock.into()) + if !stdout && !stderr { + return Ok(()); + } + + self.sock_3dslink = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) }; + if self.sock_3dslink < 0 { + Err(Error::from_errno()) } else { - self.sock_3dslink = sock; Ok(()) } } @@ -92,7 +95,7 @@ impl Drop for Soc { fn drop(&mut self) { if self.sock_3dslink >= 0 { unsafe { - libc::closesocket(self.sock_3dslink); + libc::close(self.sock_3dslink); } } } @@ -101,7 +104,6 @@ impl Drop for Soc { #[cfg(test)] mod tests { use super::*; - use crate::Error; #[test] fn soc_duplicate() { diff --git a/ctru-sys/src/lib.rs b/ctru-sys/src/lib.rs index 454a380..595cbd3 100644 --- a/ctru-sys/src/lib.rs +++ b/ctru-sys/src/lib.rs @@ -10,3 +10,10 @@ mod bindings; pub use bindings::*; pub use result::*; + +/// In lieu of a proper errno function exposed by libc +/// (), this will retrieve the +/// last error set in the global reentrancy struct. +pub unsafe fn errno() -> s32 { + (*__getreent())._errno +}