// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 #![allow(unsafe_code)] use alloc::rc::Rc; use core::ffi::c_void; use super::DataTransfer; use crate::SharedString; #[cfg(feature = "std")] use crate::SharedVector; use crate::api::Image; #[cfg(feature = "std")] use crate::slice::Slice; // The unit of a path's native representation, matching C++'s // `std::filesystem::path::value_type`: UTF-16 code units on Windows, bytes // elsewhere. Paths cross the FFI as slices of this type so that any valid // path round-trips losslessly. #[cfg(all(feature = "std", windows))] pub type PathValueType = u16; #[cfg(all(feature = "std", not(windows)))] pub type PathValueType = u8; #[cfg(feature = "std")] fn path_from_units(units: &[PathValueType]) -> std::path::PathBuf { #[cfg(unix)] { use std::os::unix::ffi::OsStrExt; std::ffi::OsStr::from_bytes(units).into() } #[cfg(windows)] { use std::os::windows::ffi::OsStringExt; std::ffi::OsString::from_wide(units).into() } #[cfg(not(any(unix, windows)))] { alloc::string::String::from_utf8_lossy(units).into_owned().into() } } #[cfg(feature = "std")] fn path_to_units(path: &std::path::Path) -> SharedVector { #[cfg(unix)] { use std::os::unix::ffi::OsStrExt; path.as_os_str().as_bytes().into() } #[cfg(windows)] { use std::os::windows::ffi::OsStrExt; path.as_os_str().encode_wide().collect() } #[cfg(not(any(unix, windows)))] { path.to_string_lossy().as_bytes().into() } } /// Opaque placeholder used by C++ to reserve storage with the same size and /// alignment as Rust's `DataTransfer`. The actual `DataTransfer` contains /// `Option>` fields whose layout cannot be expressed via cbindgen, so /// C++ never inspects these fields directly: copy/destruction goes through /// the `slint_data_transfer_*` FFI functions below, which operate on a real /// `DataTransfer`. /// /// The three pointer-sized fields correspond to: /// - `_rc_inner`: thin `Rc` pointer for `Option>` (null = `None`) /// - `_rc_any_0`/`_rc_any_1`: data + vtable pointers of the /// `Option>` user data fat pointer (null data = `None`) #[repr(C)] pub struct DataTransferOpaque { _rc_inner: *mut core::ffi::c_void, _rc_any_0: *mut core::ffi::c_void, _rc_any_1: *mut core::ffi::c_void, } static_assertions::assert_eq_align!(DataTransferOpaque, DataTransfer); static_assertions::assert_eq_size!(DataTransferOpaque, DataTransfer); /// Default-construct a `DataTransfer` in place at `out`. /// /// # Safety /// `out` must be valid for writes of `DataTransfer` and must not currently /// hold an initialized `DataTransfer` (otherwise the previous value is leaked). #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_data_transfer_init_default(out: *mut DataTransfer) { unsafe { core::ptr::write(out, DataTransfer::default()) } } /// Drop a `DataTransfer` in place. /// /// # Safety /// `d` must point to an initialized `DataTransfer` and must not be used after /// this call returns. #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_data_transfer_drop(d: *mut DataTransfer) { unsafe { core::ptr::drop_in_place(d) } } /// Clone `src` into the uninitialized memory at `out`. /// /// # Safety /// `out` must be valid for writes of `DataTransfer` and must not currently /// hold an initialized `DataTransfer` (otherwise the previous value is leaked). #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_data_transfer_clone(out: *mut DataTransfer, src: &DataTransfer) { unsafe { core::ptr::write(out, src.clone()) } } /// Compare two `DataTransfer` values for equality (same semantics as /// `::eq`). #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_eq(a: &DataTransfer, b: &DataTransfer) -> bool { a == b } /// Set the plain text representation of `d` to a clone of `text`. /// /// An empty `text` clears the previously-set plain text instead of storing it. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_set_plain_text(d: &mut DataTransfer, text: &SharedString) { d.set_plain_text(text.clone()); } /// Set the image representation of `d` to a clone of `image`. /// /// A default-constructed `image` clears the previously-set image instead of storing it. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_set_image(d: &mut DataTransfer, image: &Image) { d.set_image(image.clone()); } /// Set the file path list of `d`. Each path is a slice of its native /// representation units (see `PathValueType`). /// /// An empty `paths` clears the previously-set file paths instead of storing it. #[cfg(feature = "std")] #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_set_file_paths( d: &mut DataTransfer, paths: Slice>, ) { d.set_file_paths_vec(paths.iter().map(|path| path_from_units(path.as_slice())).collect()); } /// Returns `true` if `d` advertises a plain text representation. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_has_plain_text(d: &DataTransfer) -> bool { d.has_plain_text() } /// Returns `true` if `d` advertises an image representation. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_has_image(d: &DataTransfer) -> bool { d.has_image() } /// Returns `true` if `d` advertises a list of file paths. #[cfg(feature = "std")] #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_has_file_paths(d: &DataTransfer) -> bool { d.has_file_paths() } /// Returns `true` if `d` carries no data: no plain text, no image, no file paths, /// and no user data. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_is_empty(d: &DataTransfer) -> bool { d.is_empty() } /// If `d` has a plain text representation, write a clone of it into `out` and /// return `true`. Otherwise leave `out` unchanged and return `false`. /// /// `out` must point to an initialized `SharedString`. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_plain_text(d: &DataTransfer, out: &mut SharedString) -> bool { match d.plain_text() { Ok(s) => { *out = s; true } Err(_) => false, } } /// If `d` has an image representation, write a clone of it into `out` and /// return `true`. Otherwise leave `out` unchanged and return `false`. /// /// `out` must point to an initialized `Image`. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_image(d: &DataTransfer, out: &mut Image) -> bool { match d.image() { Ok(i) => { *out = i; true } Err(_) => false, } } /// If `d` has a list of file paths, write them into `out` and return `true`. /// Otherwise leave `out` unchanged and return `false`. Each path is a vector /// of its native representation units (see `PathValueType`). /// /// `out` must point to an initialized `SharedVector>`. #[cfg(feature = "std")] #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_file_paths( d: &DataTransfer, out: &mut SharedVector>, ) -> bool { match d.file_paths() { Ok(paths) => { *out = paths.map(path_to_units).collect(); true } Err(_) => false, } } /// C++-owned user-data handle stored in a `DataTransfer`. struct CppUserData { handle: *mut c_void, drop_fn: unsafe extern "C" fn(*mut c_void), } impl Drop for CppUserData { fn drop(&mut self) { unsafe { (self.drop_fn)(self.handle) } } } /// Store `handle` as the user data of `d`. `drop_fn(handle)` runs when `d` is dropped. #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_data_transfer_set_user_data( d: &mut DataTransfer, handle: *mut c_void, drop_fn: unsafe extern "C" fn(*mut c_void), ) { d.set_user_data(Rc::new(CppUserData { handle, drop_fn })); } /// Write a borrowed pointer to `d`'s C++ user-data handle into `out_handle`. /// Returns `false` if `d` has no C++ user data. #[unsafe(no_mangle)] pub unsafe extern "C" fn slint_data_transfer_user_data( d: &DataTransfer, out_handle: *mut *const c_void, ) -> bool { let Some(any) = d.user_data() else { return false }; let Some(cpp) = any.downcast_ref::() else { return false }; unsafe { *out_handle = cpp.handle as *const c_void }; true } /// Clear the user data of `d`, if any. #[unsafe(no_mangle)] pub extern "C" fn slint_data_transfer_clear_user_data(d: &mut DataTransfer) { d.user_data = None; }