From cb375f88a1c253acbd837e0fa8c5829bdb15558c Mon Sep 17 00:00:00 2001 From: neo Date: Fri, 13 Feb 2026 09:54:00 -0500 Subject: [PATCH] feat(springboard): get device orientation (#65) --- ffi/src/springboardservices.rs | 36 +++++++++++++ idevice/src/services/springboardservices.rs | 60 +++++++++++++++++++++ tools/src/springboardservices.rs | 11 ++++ 3 files changed, 107 insertions(+) diff --git a/ffi/src/springboardservices.rs b/ffi/src/springboardservices.rs index 4145ebd..a56709f 100644 --- a/ffi/src/springboardservices.rs +++ b/ffi/src/springboardservices.rs @@ -227,6 +227,42 @@ pub unsafe extern "C" fn springboard_services_get_lock_screen_wallpaper_preview( } } +/// Gets the current interface orientation of the device +/// +/// # Arguments +/// * `client` - A valid SpringBoardServicesClient handle +/// * `out_orientation` - On success, will contain the orientation value (0-4) +/// +/// # Returns +/// An IdeviceFfiError on error, null on success +/// +/// # Safety +/// `client` must be a valid pointer to a handle allocated by this library +/// `out_orientation` must be a valid, non-null pointer +#[unsafe(no_mangle)] +pub unsafe extern "C" fn springboard_services_get_interface_orientation( + client: *mut SpringBoardServicesClientHandle, + out_orientation: *mut u8, +) -> *mut IdeviceFfiError { + if client.is_null() || out_orientation.is_null() { + tracing::error!("Invalid arguments: {client:?}, {out_orientation:?}"); + return ffi_err!(IdeviceError::FfiInvalidArg); + } + let client = unsafe { &mut *client }; + + let res = run_sync(async { client.0.get_interface_orientation().await }); + + match res { + Ok(orientation) => { + unsafe { + *out_orientation = orientation as u8; + } + null_mut() + } + Err(e) => ffi_err!(e), + } +} + /// Frees an SpringBoardServicesClient handle /// /// # Arguments diff --git a/idevice/src/services/springboardservices.rs b/idevice/src/services/springboardservices.rs index 11b4b9d..3e89004 100644 --- a/idevice/src/services/springboardservices.rs +++ b/idevice/src/services/springboardservices.rs @@ -5,6 +5,22 @@ use crate::{Idevice, IdeviceError, IdeviceService, obf, utils::plist::truncate_dates_to_seconds}; +/// Orientation of the device +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum InterfaceOrientation { + /// Orientation is unknown or cannot be determined + Unknown = 0, + /// Portrait mode (normal vertical) + Portrait = 1, + /// Portrait mode upside down + PortraitUpsideDown = 2, + /// Landscape with home button on the right (notch to the left) + LandscapeRight = 3, + /// Landscape with home button on the left (notch to the right) + LandscapeLeft = 4, +} + /// Client for interacting with the iOS SpringBoard services /// /// This service provides access to home screen and app icon functionality, @@ -198,6 +214,7 @@ impl SpringBoardServicesClient { self.idevice.send_plist(req).await?; Ok(()) } + /// Gets the home screen wallpaper preview as PNG data /// /// This gets a rendered preview of the home screen wallpaper. @@ -265,4 +282,47 @@ impl SpringBoardServicesClient { _ => Err(IdeviceError::UnexpectedResponse), } } + + /// Gets the current interface orientation of the device + /// + /// This gets which way the device is currently facing + /// + /// # Returns + /// The current `InterfaceOrientation` of the device + /// + /// # Errors + /// Returns `IdeviceError` if: + /// - Communication fails + /// - The device doesn't support this command + /// - The response format is unexpected + /// + /// # Example + /// ```rust + /// let orientation = client.get_interface_orientation().await?; + /// println!("Device orientation: {:?}", orientation); + /// ``` + pub async fn get_interface_orientation( + &mut self, + ) -> Result { + let req = crate::plist!({ + "command": "getInterfaceOrientation", + }); + self.idevice.send_plist(req).await?; + + let res = self.idevice.read_plist().await?; + let orientation_value = res + .get("interfaceOrientation") + .and_then(|v| v.as_unsigned_integer()) + .ok_or(IdeviceError::UnexpectedResponse)?; + + let orientation = match orientation_value { + 1 => InterfaceOrientation::Portrait, + 2 => InterfaceOrientation::PortraitUpsideDown, + 3 => InterfaceOrientation::LandscapeRight, + 4 => InterfaceOrientation::LandscapeLeft, + _ => InterfaceOrientation::Unknown, + }; + + Ok(orientation) + } } diff --git a/tools/src/springboardservices.rs b/tools/src/springboardservices.rs index f997c2f..1165b6a 100644 --- a/tools/src/springboardservices.rs +++ b/tools/src/springboardservices.rs @@ -45,6 +45,10 @@ pub fn register() -> JkCommand { .with_argument(JkArgument::new().required(true)), ), ) + .with_subcommand( + "get_interface_orientation", + JkCommand::new().help("Gets the device's current screen orientation"), + ) .subcommand_required(true) } @@ -102,6 +106,13 @@ pub async fn main(arguments: &CollectedArguments, provider: Box { + let orientation = sbc + .get_interface_orientation() + .await + .expect("Failed to get interface orientation"); + println!("{:?}", orientation); + } _ => unreachable!(), } }