feat(springboard): add wallpaper preview command support (#64)

* feat(springboard): add wallpaper preview command support

* Use subargs to switch between preview type in sb cli

---------

Co-authored-by: Jackson Coxson <jkcoxson@gmail.com>
This commit is contained in:
neo
2026-02-09 21:14:16 -05:00
committed by GitHub
parent 38a3a558b5
commit c5aa731ee5
3 changed files with 188 additions and 0 deletions

View File

@@ -137,6 +137,96 @@ pub unsafe extern "C" fn springboard_services_get_icon(
} }
} }
/// Gets the home screen wallpaper preview as PNG image
///
/// # Arguments
/// * `client` - A valid SpringBoardServicesClient handle
/// * `out_result` - On success, will be set to point to newly allocated png image
/// * `out_result_len` - On success, will contain the size of the data in bytes
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `out_result` and `out_result_len` must be valid, non-null pointers
#[unsafe(no_mangle)]
pub unsafe extern "C" fn springboard_services_get_home_screen_wallpaper_preview(
client: *mut SpringBoardServicesClientHandle,
out_result: *mut *mut c_void,
out_result_len: *mut libc::size_t,
) -> *mut IdeviceFfiError {
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
tracing::error!("Invalid arguments: {client:?}, {out_result:?}");
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let client = unsafe { &mut *client };
let res: Result<Vec<u8>, IdeviceError> =
run_sync(async { client.0.get_home_screen_wallpaper_preview_pngdata().await });
match res {
Ok(r) => {
let len = r.len();
let boxed_slice = r.into_boxed_slice();
let ptr = boxed_slice.as_ptr();
std::mem::forget(boxed_slice);
unsafe {
*out_result = ptr as *mut c_void;
*out_result_len = len;
}
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Gets the lock screen wallpaper preview as PNG image
///
/// # Arguments
/// * `client` - A valid SpringBoardServicesClient handle
/// * `out_result` - On success, will be set to point to newly allocated png image
/// * `out_result_len` - On success, will contain the size of the data in bytes
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `out_result` and `out_result_len` must be valid, non-null pointers
#[unsafe(no_mangle)]
pub unsafe extern "C" fn springboard_services_get_lock_screen_wallpaper_preview(
client: *mut SpringBoardServicesClientHandle,
out_result: *mut *mut c_void,
out_result_len: *mut libc::size_t,
) -> *mut IdeviceFfiError {
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
tracing::error!("Invalid arguments: {client:?}, {out_result:?}");
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let client = unsafe { &mut *client };
let res: Result<Vec<u8>, IdeviceError> =
run_sync(async { client.0.get_lock_screen_wallpaper_preview_pngdata().await });
match res {
Ok(r) => {
let len = r.len();
let boxed_slice = r.into_boxed_slice();
let ptr = boxed_slice.as_ptr();
std::mem::forget(boxed_slice);
unsafe {
*out_result = ptr as *mut c_void;
*out_result_len = len;
}
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees an SpringBoardServicesClient handle /// Frees an SpringBoardServicesClient handle
/// ///
/// # Arguments /// # Arguments

View File

@@ -198,4 +198,71 @@ impl SpringBoardServicesClient {
self.idevice.send_plist(req).await?; self.idevice.send_plist(req).await?;
Ok(()) Ok(())
} }
/// Gets the home screen wallpaper preview as PNG data
///
/// This gets a rendered preview of the home screen wallpaper.
///
/// # Returns
/// The raw PNG data of the home screen wallpaper preview
///
/// # Errors
/// Returns `IdeviceError` if:
/// - Communication fails
/// - The device rejects the request
/// - The image is malformed/corupted
///
/// # Example
/// ```rust
/// let wallpaper = client.get_home_screen_wallpaper_preview_pngdata().await?;
/// std::fs::write("home.png", wallpaper)?;
/// ```
pub async fn get_home_screen_wallpaper_preview_pngdata(
&mut self,
) -> Result<Vec<u8>, IdeviceError> {
let req = crate::plist!({
"command": "getWallpaperPreviewImage",
"wallpaperName": "homescreen",
});
self.idevice.send_plist(req).await?;
let mut res = self.idevice.read_plist().await?;
match res.remove("pngData") {
Some(plist::Value::Data(res)) => Ok(res),
_ => Err(IdeviceError::UnexpectedResponse),
}
}
/// Gets the lock screen wallpaper preview as PNG data
///
/// This gets a rendered preview of the lock screen wallpaper.
///
/// # Returns
/// The raw PNG data of the lock screen wallpaper preview
///
/// # Errors
/// Returns `IdeviceError` if:
/// - Communication fails
/// - The device rejects the request
/// - The image is malformed/corupted
///
/// # Example
/// ```rust
/// let wallpaper = client.get_lock_screen_wallpaper_preview_pngdata().await?;
/// std::fs::write("lock.png", wallpaper)?;
/// ```
pub async fn get_lock_screen_wallpaper_preview_pngdata(
&mut self,
) -> Result<Vec<u8>, IdeviceError> {
let req = crate::plist!({
"command": "getWallpaperPreviewImage",
"wallpaperName": "lockscreen",
});
self.idevice.send_plist(req).await?;
let mut res = self.idevice.read_plist().await?;
match res.remove("pngData") {
Some(plist::Value::Data(res)) => Ok(res),
_ => Err(IdeviceError::UnexpectedResponse),
}
}
} }

View File

@@ -32,6 +32,19 @@ pub fn register() -> JkCommand {
.required(true), .required(true),
), ),
) )
.with_subcommand(
"get_wallpaper_preview",
JkCommand::new()
.help("Gets wallpaper preview")
.with_subcommand("homescreen", JkCommand::new())
.with_subcommand("lockscreen", JkCommand::new())
.subcommand_required(true)
.with_flag(
JkFlag::new("save")
.with_help("Path to save the wallpaper PNG file, or preview.png by default")
.with_argument(JkArgument::new().required(true)),
),
)
.subcommand_required(true) .subcommand_required(true)
} }
@@ -71,6 +84,24 @@ pub async fn main(arguments: &CollectedArguments, provider: Box<dyn IdeviceProvi
.await .await
.expect("Failed to set icon state"); .expect("Failed to set icon state");
} }
"get_wallpaper_preview" => {
let (wallpaper_type, _) = sub_args.first_subcommand().unwrap();
let wallpaper = match wallpaper_type.as_str() {
"homescreen" => sbc.get_home_screen_wallpaper_preview_pngdata().await,
"lockscreen" => sbc.get_lock_screen_wallpaper_preview_pngdata().await,
_ => panic!("Invalid wallpaper type. Use 'homescreen' or 'lockscreen'"),
}
.expect("Failed to get wallpaper preview");
let save_path = sub_args
.get_flag::<String>("save")
.unwrap_or("preview.png".to_string());
tokio::fs::write(&save_path, wallpaper)
.await
.expect("Failed to save wallpaper");
}
_ => unreachable!(), _ => unreachable!(),
} }
} }