Skip to main content

slint_interpreter/
ffi.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4// cSpell: ignore stru
5use crate::dynamic_item_tree::ErasedItemTreeBox;
6
7use super::*;
8use core::ptr::NonNull;
9use i_slint_core::model::{Model, ModelNotify, ModelRc, SharedVectorModel};
10use i_slint_core::slice::Slice;
11use i_slint_core::window::WindowAdapter;
12use smol_str::SmolStr;
13use std::ffi::c_void;
14use std::path::PathBuf;
15use std::rc::Rc;
16use vtable::VRef;
17
18/// Construct a new Value in the given memory location
19#[unsafe(no_mangle)]
20pub extern "C" fn slint_interpreter_value_new() -> Box<Value> {
21    Box::new(Value::default())
22}
23
24/// Construct a new Value in the given memory location
25#[unsafe(no_mangle)]
26pub extern "C" fn slint_interpreter_value_clone(other: &Value) -> Box<Value> {
27    Box::new(other.clone())
28}
29
30/// Destruct the value in that memory location
31#[unsafe(no_mangle)]
32pub extern "C" fn slint_interpreter_value_destructor(val: Box<Value>) {
33    drop(val);
34}
35
36#[unsafe(no_mangle)]
37pub extern "C" fn slint_interpreter_value_eq(a: &Value, b: &Value) -> bool {
38    a == b
39}
40
41/// Construct a new Value in the given memory location as string
42#[unsafe(no_mangle)]
43pub extern "C" fn slint_interpreter_value_new_string(str: &SharedString) -> Box<Value> {
44    Box::new(Value::String(str.clone()))
45}
46
47/// Construct a new Value in the given memory location as double
48#[unsafe(no_mangle)]
49pub extern "C" fn slint_interpreter_value_new_double(double: f64) -> Box<Value> {
50    Box::new(Value::Number(double))
51}
52
53/// Construct a new Value in the given memory location as bool
54#[unsafe(no_mangle)]
55pub extern "C" fn slint_interpreter_value_new_bool(b: bool) -> Box<Value> {
56    Box::new(Value::Bool(b))
57}
58
59/// Construct a new Value in the given memory location as array model
60#[unsafe(no_mangle)]
61pub extern "C" fn slint_interpreter_value_new_array_model(
62    a: &SharedVector<Box<Value>>,
63) -> Box<Value> {
64    let vec = a.iter().map(|vb| vb.as_ref().clone()).collect::<SharedVector<_>>();
65    Box::new(Value::Model(ModelRc::new(SharedVectorModel::from(vec))))
66}
67
68/// Construct a new Value in the given memory location as Brush
69#[unsafe(no_mangle)]
70pub extern "C" fn slint_interpreter_value_new_brush(brush: &Brush) -> Box<Value> {
71    Box::new(Value::Brush(brush.clone()))
72}
73
74/// Construct a new Value in the given memory location as Struct
75#[unsafe(no_mangle)]
76pub extern "C" fn slint_interpreter_value_new_struct(struc: &StructOpaque) -> Box<Value> {
77    Box::new(Value::Struct(struc.as_struct().clone()))
78}
79
80/// Construct a new Value in the given memory location as image
81#[unsafe(no_mangle)]
82pub extern "C" fn slint_interpreter_value_new_image(img: &Image) -> Box<Value> {
83    Box::new(Value::Image(img.clone()))
84}
85
86/// Construct a new Value containing a model in the given memory location
87#[unsafe(no_mangle)]
88pub unsafe extern "C" fn slint_interpreter_value_new_model(
89    model: NonNull<u8>,
90    vtable: &ModelAdaptorVTable,
91) -> Box<Value> {
92    Box::new(Value::Model(ModelRc::new(ModelAdaptorWrapper(unsafe {
93        vtable::VBox::from_raw(NonNull::from(vtable), model)
94    }))))
95}
96
97/// If the value contains a model set from [`slint_interpreter_value_new_model]` with the same vtable pointer,
98/// return the model that was set.
99/// Returns a null ptr otherwise
100#[unsafe(no_mangle)]
101pub extern "C" fn slint_interpreter_value_to_model(
102    val: &Value,
103    vtable: &ModelAdaptorVTable,
104) -> *const u8 {
105    if let Value::Model(m) = val
106        && let Some(m) = m.as_any().downcast_ref::<ModelAdaptorWrapper>()
107        && core::ptr::eq(m.0.get_vtable() as *const _, vtable as *const _)
108    {
109        return m.0.as_ptr();
110    }
111    core::ptr::null()
112}
113
114#[unsafe(no_mangle)]
115pub extern "C" fn slint_interpreter_value_type(val: &Value) -> ValueType {
116    val.value_type()
117}
118
119#[unsafe(no_mangle)]
120pub extern "C" fn slint_interpreter_value_to_string(val: &Value) -> Option<&SharedString> {
121    match val {
122        Value::String(v) => Some(v),
123        _ => None,
124    }
125}
126
127#[unsafe(no_mangle)]
128pub extern "C" fn slint_interpreter_value_to_number(val: &Value) -> Option<&f64> {
129    match val {
130        Value::Number(v) => Some(v),
131        _ => None,
132    }
133}
134
135#[unsafe(no_mangle)]
136pub extern "C" fn slint_interpreter_value_to_bool(val: &Value) -> Option<&bool> {
137    match val {
138        Value::Bool(v) => Some(v),
139        _ => None,
140    }
141}
142
143/// Extracts a `SharedVector<ValueOpaque>` out of the given value `val`, writes that into the
144/// `out` parameter and returns true; returns false if the value does not hold an extractable
145/// array.
146#[unsafe(no_mangle)]
147#[allow(clippy::borrowed_box)]
148pub extern "C" fn slint_interpreter_value_to_array(
149    val: &Box<Value>,
150    out: &mut SharedVector<Box<Value>>,
151) -> bool {
152    match val.as_ref() {
153        Value::Model(m) => {
154            let vec = m.iter().map(Box::new).collect::<SharedVector<_>>();
155            *out = vec;
156            true
157        }
158        _ => false,
159    }
160}
161
162#[unsafe(no_mangle)]
163pub extern "C" fn slint_interpreter_value_to_brush(val: &Value) -> Option<&Brush> {
164    match val {
165        Value::Brush(b) => Some(b),
166        _ => None,
167    }
168}
169
170#[unsafe(no_mangle)]
171pub extern "C" fn slint_interpreter_value_to_struct(val: &Value) -> *const StructOpaque {
172    match val {
173        Value::Struct(s) => s as *const Struct as *const StructOpaque,
174        _ => std::ptr::null(),
175    }
176}
177
178#[unsafe(no_mangle)]
179pub extern "C" fn slint_interpreter_value_to_image(val: &Value) -> Option<&Image> {
180    match val {
181        Value::Image(img) => Some(img),
182        _ => None,
183    }
184}
185
186/// Construct a new Value containing a DataTransfer
187#[unsafe(no_mangle)]
188pub extern "C" fn slint_interpreter_value_new_data_transfer(
189    data: &i_slint_core::data_transfer::DataTransfer,
190) -> Box<Value> {
191    Box::new(Value::DataTransfer(data.clone()))
192}
193
194#[unsafe(no_mangle)]
195pub extern "C" fn slint_interpreter_value_to_data_transfer(
196    val: &Value,
197) -> Option<&i_slint_core::data_transfer::DataTransfer> {
198    match val {
199        Value::DataTransfer(data) => Some(data),
200        _ => None,
201    }
202}
203
204/// Construct a new Value containing a Keys
205#[unsafe(no_mangle)]
206pub extern "C" fn slint_interpreter_value_new_keys(keys: &i_slint_core::input::Keys) -> Box<Value> {
207    Box::new(Value::Keys(keys.clone()))
208}
209
210#[unsafe(no_mangle)]
211pub extern "C" fn slint_interpreter_value_to_keys(
212    val: &Value,
213) -> Option<&i_slint_core::input::Keys> {
214    match val {
215        Value::Keys(keys) => Some(keys),
216        _ => None,
217    }
218}
219
220/// Construct a new Value containing a StyledText
221#[unsafe(no_mangle)]
222pub extern "C" fn slint_interpreter_value_new_styled_text(
223    text: &i_slint_core::styled_text::StyledText,
224) -> Box<Value> {
225    Box::new(Value::StyledText(text.clone()))
226}
227
228#[unsafe(no_mangle)]
229pub extern "C" fn slint_interpreter_value_to_styled_text(
230    val: &Value,
231) -> Option<&i_slint_core::styled_text::StyledText> {
232    match val {
233        Value::StyledText(text) => Some(text),
234        _ => None,
235    }
236}
237
238#[unsafe(no_mangle)]
239pub extern "C" fn slint_interpreter_value_enum_to_string(
240    val: &Value,
241    result: &mut SharedString,
242) -> bool {
243    match val {
244        Value::EnumerationValue(_, value) => {
245            *result = SharedString::from(value);
246            true
247        }
248        _ => false,
249    }
250}
251
252#[unsafe(no_mangle)]
253pub extern "C" fn slint_interpreter_value_new_enum(
254    name: Slice<u8>,
255    value: Slice<u8>,
256) -> Box<Value> {
257    Box::new(Value::EnumerationValue(
258        std::str::from_utf8(&name).unwrap().to_string(),
259        std::str::from_utf8(&value).unwrap().to_string(),
260    ))
261}
262
263#[repr(C)]
264#[cfg(target_pointer_width = "64")]
265pub struct StructOpaque([usize; 6]);
266#[repr(C)]
267#[cfg(target_pointer_width = "32")]
268pub struct StructOpaque([u64; 4]);
269const _: [(); std::mem::size_of::<StructOpaque>()] = [(); std::mem::size_of::<Struct>()];
270const _: [(); std::mem::align_of::<StructOpaque>()] = [(); std::mem::align_of::<Struct>()];
271
272impl StructOpaque {
273    fn as_struct(&self) -> &Struct {
274        // Safety: there should be no way to construct a StructOpaque without it holding an actual Struct
275        unsafe { std::mem::transmute::<&StructOpaque, &Struct>(self) }
276    }
277    fn as_struct_mut(&mut self) -> &mut Struct {
278        // Safety: there should be no way to construct a StructOpaque without it holding an actual Struct
279        unsafe { std::mem::transmute::<&mut StructOpaque, &mut Struct>(self) }
280    }
281}
282
283/// Construct a new Struct in the given memory location
284#[unsafe(no_mangle)]
285pub unsafe extern "C" fn slint_interpreter_struct_new(val: *mut StructOpaque) {
286    unsafe { std::ptr::write(val as *mut Struct, Struct::default()) }
287}
288
289/// Construct a new Struct in the given memory location
290#[unsafe(no_mangle)]
291pub unsafe extern "C" fn slint_interpreter_struct_clone(
292    other: &StructOpaque,
293    val: *mut StructOpaque,
294) {
295    unsafe { std::ptr::write(val as *mut Struct, other.as_struct().clone()) }
296}
297
298/// Destruct the struct in that memory location
299#[unsafe(no_mangle)]
300pub unsafe extern "C" fn slint_interpreter_struct_destructor(val: *mut StructOpaque) {
301    drop(unsafe { std::ptr::read(val as *mut Struct) })
302}
303
304#[unsafe(no_mangle)]
305pub extern "C" fn slint_interpreter_struct_get_field(
306    stru: &StructOpaque,
307    name: Slice<u8>,
308) -> *mut Value {
309    if let Some(value) = stru.as_struct().get_field(std::str::from_utf8(&name).unwrap()) {
310        Box::into_raw(Box::new(value.clone()))
311    } else {
312        std::ptr::null_mut()
313    }
314}
315
316#[unsafe(no_mangle)]
317pub extern "C" fn slint_interpreter_struct_set_field(
318    stru: &mut StructOpaque,
319    name: Slice<u8>,
320    value: &Value,
321) {
322    stru.as_struct_mut().set_field(std::str::from_utf8(&name).unwrap().into(), value.clone())
323}
324
325type StructIterator<'a> = std::collections::hash_map::Iter<'a, SmolStr, Value>;
326#[repr(C)]
327pub struct StructIteratorOpaque<'a>([usize; 5], std::marker::PhantomData<StructIterator<'a>>);
328const _: [(); std::mem::size_of::<StructIteratorOpaque>()] =
329    [(); std::mem::size_of::<StructIterator>()];
330const _: [(); std::mem::align_of::<StructIteratorOpaque>()] =
331    [(); std::mem::align_of::<StructIterator>()];
332
333#[unsafe(no_mangle)]
334pub unsafe extern "C" fn slint_interpreter_struct_iterator_destructor(
335    val: *mut StructIteratorOpaque,
336) {
337    #[allow(clippy::drop_non_drop)] // the drop is a no-op but we still want to be explicit
338    drop(unsafe { std::ptr::read(val as *mut StructIterator) })
339}
340
341/// Advance the iterator and return the next value, or a null pointer
342#[unsafe(no_mangle)]
343pub unsafe extern "C" fn slint_interpreter_struct_iterator_next<'a>(
344    iter: &'a mut StructIteratorOpaque,
345    k: &mut Slice<'a, u8>,
346) -> *mut Value {
347    if let Some((str, val)) =
348        unsafe { (*(iter as *mut StructIteratorOpaque as *mut StructIterator)).next() }
349    {
350        *k = Slice::from_slice(str.as_bytes());
351        Box::into_raw(Box::new(val.clone()))
352    } else {
353        *k = Slice::default();
354        std::ptr::null_mut()
355    }
356}
357
358#[unsafe(no_mangle)]
359pub extern "C" fn slint_interpreter_struct_make_iter(
360    stru: &StructOpaque,
361) -> StructIteratorOpaque<'_> {
362    let ret_it: StructIterator = stru.as_struct().0.iter();
363    unsafe {
364        let mut r = std::mem::MaybeUninit::<StructIteratorOpaque>::uninit();
365        std::ptr::write(r.as_mut_ptr() as *mut StructIterator, ret_it);
366        r.assume_init()
367    }
368}
369
370/// Get a property. Returns a null pointer if the property does not exist.
371#[unsafe(no_mangle)]
372pub extern "C" fn slint_interpreter_component_instance_get_property(
373    inst: &ErasedItemTreeBox,
374    name: Slice<u8>,
375) -> *mut Value {
376    generativity::make_guard!(guard);
377    let comp = inst.unerase(guard);
378    match comp
379        .description()
380        .get_property(comp.borrow(), &normalize_identifier(std::str::from_utf8(&name).unwrap()))
381    {
382        Ok(val) => Box::into_raw(Box::new(val)),
383        Err(_) => std::ptr::null_mut(),
384    }
385}
386
387#[unsafe(no_mangle)]
388pub extern "C" fn slint_interpreter_component_instance_set_property(
389    inst: &ErasedItemTreeBox,
390    name: Slice<u8>,
391    val: &Value,
392) -> bool {
393    generativity::make_guard!(guard);
394    let comp = inst.unerase(guard);
395    comp.description()
396        .set_property(
397            comp.borrow(),
398            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
399            val.clone(),
400        )
401        .is_ok()
402}
403
404/// Invoke a callback or function. Returns raw boxed value on success and null ptr on failure.
405#[unsafe(no_mangle)]
406pub extern "C" fn slint_interpreter_component_instance_invoke(
407    inst: &ErasedItemTreeBox,
408    name: Slice<u8>,
409    args: Slice<Box<Value>>,
410) -> *mut Value {
411    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
412    generativity::make_guard!(guard);
413    let comp = inst.unerase(guard);
414    match comp.description().invoke(
415        comp.borrow(),
416        &normalize_identifier(std::str::from_utf8(&name).unwrap()),
417        args.as_slice(),
418    ) {
419        Ok(val) => Box::into_raw(Box::new(val)),
420        Err(_) => std::ptr::null_mut(),
421    }
422}
423
424/// Wrap the user_data provided by the native code and call the drop function on Drop.
425///
426/// Safety: user_data must be a pointer that can be destroyed by the drop_user_data function.
427/// callback must be a valid callback that initialize the `ret`
428pub struct CallbackUserData {
429    user_data: *mut c_void,
430    drop_user_data: Option<extern "C" fn(*mut c_void)>,
431    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
432}
433
434impl Drop for CallbackUserData {
435    fn drop(&mut self) {
436        if let Some(x) = self.drop_user_data {
437            x(self.user_data)
438        }
439    }
440}
441
442impl CallbackUserData {
443    pub unsafe fn new(
444        user_data: *mut c_void,
445        drop_user_data: Option<extern "C" fn(*mut c_void)>,
446        callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
447    ) -> Self {
448        Self { user_data, drop_user_data, callback }
449    }
450
451    pub fn call(&self, args: &[Value]) -> Value {
452        let args = args.iter().map(|v| v.clone().into()).collect::<Vec<_>>();
453        (self.callback)(self.user_data, Slice::from_slice(args.as_ref())).as_ref().clone()
454    }
455}
456
457/// Set a handler for the callback.
458/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
459#[unsafe(no_mangle)]
460pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
461    inst: &ErasedItemTreeBox,
462    name: Slice<u8>,
463    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
464    user_data: *mut c_void,
465    drop_user_data: Option<extern "C" fn(*mut c_void)>,
466) -> bool {
467    let ud = unsafe { CallbackUserData::new(user_data, drop_user_data, callback) };
468
469    generativity::make_guard!(guard);
470    let comp = inst.unerase(guard);
471    comp.description()
472        .set_callback_handler(
473            comp.borrow(),
474            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
475            Box::new(move |args| ud.call(args)),
476        )
477        .is_ok()
478}
479
480/// Get a global property. Returns a raw boxed value on success; nullptr otherwise.
481#[unsafe(no_mangle)]
482pub unsafe extern "C" fn slint_interpreter_component_instance_get_global_property(
483    inst: &ErasedItemTreeBox,
484    global: Slice<u8>,
485    property_name: Slice<u8>,
486) -> *mut Value {
487    generativity::make_guard!(guard);
488    let comp = inst.unerase(guard);
489    match comp
490        .description()
491        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
492        .and_then(|g| {
493            g.as_ref()
494                .get_property(&normalize_identifier(std::str::from_utf8(&property_name).unwrap()))
495        }) {
496        Ok(val) => Box::into_raw(Box::new(val)),
497        Err(_) => std::ptr::null_mut(),
498    }
499}
500
501#[unsafe(no_mangle)]
502pub extern "C" fn slint_interpreter_component_instance_set_global_property(
503    inst: &ErasedItemTreeBox,
504    global: Slice<u8>,
505    property_name: Slice<u8>,
506    val: &Value,
507) -> bool {
508    generativity::make_guard!(guard);
509    let comp = inst.unerase(guard);
510    comp.description()
511        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
512        .and_then(|g| {
513            g.as_ref()
514                .set_property(
515                    &normalize_identifier(std::str::from_utf8(&property_name).unwrap()),
516                    val.clone(),
517                )
518                .map_err(|_| ())
519        })
520        .is_ok()
521}
522
523/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
524#[unsafe(no_mangle)]
525pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callback(
526    inst: &ErasedItemTreeBox,
527    global: Slice<u8>,
528    name: Slice<u8>,
529    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
530    user_data: *mut c_void,
531    drop_user_data: Option<extern "C" fn(*mut c_void)>,
532) -> bool {
533    let ud = unsafe { CallbackUserData::new(user_data, drop_user_data, callback) };
534
535    generativity::make_guard!(guard);
536    let comp = inst.unerase(guard);
537    comp.description()
538        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
539        .and_then(|g| {
540            g.as_ref().set_callback_handler(
541                &normalize_identifier(std::str::from_utf8(&name).unwrap()),
542                Box::new(move |args| ud.call(args)),
543            )
544        })
545        .is_ok()
546}
547
548/// Invoke a global callback or function. Returns raw boxed value on success; nullptr otherwise.
549#[unsafe(no_mangle)]
550pub unsafe extern "C" fn slint_interpreter_component_instance_invoke_global(
551    inst: &ErasedItemTreeBox,
552    global: Slice<u8>,
553    callable_name: Slice<u8>,
554    args: Slice<Box<Value>>,
555) -> *mut Value {
556    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
557    generativity::make_guard!(guard);
558    let comp = inst.unerase(guard);
559    let callable_name = std::str::from_utf8(&callable_name).unwrap();
560    match comp
561        .description()
562        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
563        .and_then(|g| {
564            if matches!(
565                comp.description()
566                    .original
567                    .root_element
568                    .borrow()
569                    .lookup_property(callable_name)
570                    .property_type,
571                i_slint_compiler::langtype::Type::Function { .. }
572            ) {
573                g.as_ref()
574                    .eval_function(&normalize_identifier(callable_name), args.as_slice().to_vec())
575            } else {
576                g.as_ref().invoke_callback(&normalize_identifier(callable_name), args.as_slice())
577            }
578        }) {
579        Ok(val) => Box::into_raw(Box::new(val)),
580        Err(_) => std::ptr::null_mut(),
581    }
582}
583
584/// Show or hide
585#[unsafe(no_mangle)]
586pub extern "C" fn slint_interpreter_component_instance_show(
587    inst: &ErasedItemTreeBox,
588    is_visible: bool,
589) {
590    generativity::make_guard!(guard);
591    let comp = inst.unerase(guard);
592    match is_visible {
593        true => comp.borrow_instance().window_adapter().window().show().unwrap(),
594        false => comp.borrow_instance().window_adapter().window().hide().unwrap(),
595    }
596}
597
598/// Return a window for the component
599///
600/// The out pointer must be uninitialized and must be destroyed with
601/// slint_windowrc_drop after usage
602#[unsafe(no_mangle)]
603pub unsafe extern "C" fn slint_interpreter_component_instance_window(
604    inst: &ErasedItemTreeBox,
605    out: *mut *const i_slint_core::window::ffi::WindowAdapterRcOpaque,
606) {
607    assert_eq!(
608        core::mem::size_of::<Rc<dyn WindowAdapter>>(),
609        core::mem::size_of::<i_slint_core::window::ffi::WindowAdapterRcOpaque>()
610    );
611    unsafe {
612        core::ptr::write(
613            out as *mut *const Rc<dyn WindowAdapter>,
614            inst.window_adapter_ref().unwrap() as *const _,
615        )
616    }
617}
618
619/// Instantiate an instance from a definition.
620///
621/// The `out` must be uninitialized and is going to be initialized after the call
622/// and need to be destroyed with slint_interpreter_component_instance_destructor
623#[unsafe(no_mangle)]
624pub unsafe extern "C" fn slint_interpreter_component_instance_create(
625    def: &ComponentDefinitionOpaque,
626    out: *mut ComponentInstance,
627) {
628    unsafe { std::ptr::write(out, def.as_component_definition().create().unwrap()) }
629}
630
631#[unsafe(no_mangle)]
632pub unsafe extern "C" fn slint_interpreter_component_instance_component_definition(
633    inst: &ErasedItemTreeBox,
634    component_definition_ptr: *mut ComponentDefinitionOpaque,
635) {
636    generativity::make_guard!(guard);
637    let definition = ComponentDefinition { inner: inst.unerase(guard).description().into() };
638    unsafe { std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition) };
639}
640
641#[vtable::vtable]
642#[repr(C)]
643pub struct ModelAdaptorVTable {
644    pub row_count: extern "C" fn(VRef<ModelAdaptorVTable>) -> usize,
645    pub row_data: unsafe extern "C" fn(VRef<ModelAdaptorVTable>, row: usize) -> *mut Value,
646    pub set_row_data: extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, value: Box<Value>),
647    pub push_row: extern "C" fn(VRef<ModelAdaptorVTable>, value: Box<Value>),
648    pub remove_row: extern "C" fn(VRef<ModelAdaptorVTable>, row: isize),
649    pub insert_row: extern "C" fn(VRef<ModelAdaptorVTable>, row: isize, value: Box<Value>),
650    pub get_notify: extern "C" fn(VRef<'_, ModelAdaptorVTable>) -> &ModelNotifyOpaque,
651    pub drop: extern "C" fn(VRefMut<ModelAdaptorVTable>),
652}
653
654struct ModelAdaptorWrapper(vtable::VBox<ModelAdaptorVTable>);
655impl Model for ModelAdaptorWrapper {
656    type Data = Value;
657
658    fn row_count(&self) -> usize {
659        self.0.row_count()
660    }
661
662    fn row_data(&self, row: usize) -> Option<Value> {
663        let val_ptr = unsafe { self.0.row_data(row) };
664        if val_ptr.is_null() { None } else { Some(*unsafe { Box::from_raw(val_ptr) }) }
665    }
666
667    fn model_tracker(&self) -> &dyn i_slint_core::model::ModelTracker {
668        self.0.get_notify().as_model_notify()
669    }
670
671    fn set_row_data(&self, row: usize, data: Value) {
672        let val = Box::new(data);
673        self.0.set_row_data(row, val);
674    }
675
676    fn push_row(&self, data: Value) {
677        let val = Box::new(data);
678        self.0.push_row(val);
679    }
680
681    fn remove_row(&self, row: isize) {
682        self.0.remove_row(row);
683    }
684
685    fn insert_row(&self, row: isize, data: Value) {
686        let val = Box::new(data);
687        self.0.insert_row(row, val);
688    }
689
690    fn as_any(&self) -> &dyn core::any::Any {
691        self
692    }
693}
694
695#[repr(C)]
696#[cfg(target_pointer_width = "64")]
697pub struct ModelNotifyOpaque([usize; 8]);
698#[repr(C)]
699#[cfg(target_pointer_width = "32")]
700pub struct ModelNotifyOpaque([usize; 12]);
701/// Asserts that ModelNotifyOpaque is at least as large as ModelNotify, otherwise this would overflow
702const _: usize = std::mem::size_of::<ModelNotifyOpaque>() - std::mem::size_of::<ModelNotify>();
703const _: usize = std::mem::align_of::<ModelNotifyOpaque>() - std::mem::align_of::<ModelNotify>();
704
705impl ModelNotifyOpaque {
706    fn as_model_notify(&self) -> &ModelNotify {
707        // Safety: there should be no way to construct a ModelNotifyOpaque without it holding an actual ModelNotify
708        unsafe { std::mem::transmute::<&ModelNotifyOpaque, &ModelNotify>(self) }
709    }
710}
711
712/// Construct a new ModelNotifyNotify in the given memory region
713#[unsafe(no_mangle)]
714pub unsafe extern "C" fn slint_interpreter_model_notify_new(val: *mut ModelNotifyOpaque) {
715    unsafe { std::ptr::write(val as *mut ModelNotify, ModelNotify::default()) };
716}
717
718/// Destruct the value in that memory location
719#[unsafe(no_mangle)]
720pub unsafe extern "C" fn slint_interpreter_model_notify_destructor(val: *mut ModelNotifyOpaque) {
721    drop(unsafe { std::ptr::read(val as *mut ModelNotify) })
722}
723
724#[unsafe(no_mangle)]
725pub unsafe extern "C" fn slint_interpreter_model_notify_row_changed(
726    notify: &ModelNotifyOpaque,
727    row: usize,
728) {
729    notify.as_model_notify().row_changed(row);
730}
731
732#[unsafe(no_mangle)]
733pub unsafe extern "C" fn slint_interpreter_model_notify_row_added(
734    notify: &ModelNotifyOpaque,
735    row: usize,
736    count: usize,
737) {
738    notify.as_model_notify().row_added(row, count);
739}
740
741#[unsafe(no_mangle)]
742pub unsafe extern "C" fn slint_interpreter_model_notify_reset(notify: &ModelNotifyOpaque) {
743    notify.as_model_notify().reset();
744}
745
746#[unsafe(no_mangle)]
747pub unsafe extern "C" fn slint_interpreter_model_notify_row_removed(
748    notify: &ModelNotifyOpaque,
749    row: usize,
750    count: usize,
751) {
752    notify.as_model_notify().row_removed(row, count);
753}
754
755// FIXME: Figure out how to re-export the one from compilerlib
756/// DiagnosticLevel describes the severity of a diagnostic.
757#[derive(Clone)]
758#[repr(u8)]
759pub enum DiagnosticLevel {
760    /// The diagnostic belongs to an error.
761    Error,
762    /// The diagnostic belongs to a warning.
763    Warning,
764    /// The diagnostic is a note
765    Note,
766}
767
768/// Diagnostic describes the aspects of either a warning or an error, along
769/// with its location and a description. Diagnostics are typically returned by
770/// slint::interpreter::ComponentCompiler::diagnostics() in a vector.
771#[derive(Clone)]
772#[repr(C)]
773pub struct Diagnostic {
774    /// The message describing the warning or error.
775    message: SharedString,
776    /// The path to the source file where the warning or error is located.
777    source_file: SharedString,
778    /// The line within the source file. Line numbers start at 1.
779    line: usize,
780    /// The column within the source file. Column numbers start at 1.
781    column: usize,
782    /// The level of the diagnostic, such as a warning or an error.
783    level: DiagnosticLevel,
784}
785
786#[repr(transparent)]
787pub struct ComponentCompilerOpaque(#[allow(deprecated)] NonNull<ComponentCompiler>);
788
789#[allow(deprecated)]
790impl ComponentCompilerOpaque {
791    fn as_component_compiler(&self) -> &ComponentCompiler {
792        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
793        unsafe { self.0.as_ref() }
794    }
795    fn as_component_compiler_mut(&mut self) -> &mut ComponentCompiler {
796        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
797        unsafe { self.0.as_mut() }
798    }
799}
800
801#[unsafe(no_mangle)]
802#[allow(deprecated)]
803pub unsafe extern "C" fn slint_interpreter_component_compiler_new(
804    compiler: *mut ComponentCompilerOpaque,
805) {
806    unsafe {
807        *compiler = ComponentCompilerOpaque(NonNull::new_unchecked(Box::into_raw(Box::new(
808            ComponentCompiler::default(),
809        ))));
810    }
811}
812
813#[unsafe(no_mangle)]
814pub unsafe extern "C" fn slint_interpreter_component_compiler_destructor(
815    compiler: *mut ComponentCompilerOpaque,
816) {
817    drop(unsafe { Box::from_raw((*compiler).0.as_ptr()) })
818}
819
820#[unsafe(no_mangle)]
821pub unsafe extern "C" fn slint_interpreter_component_compiler_set_include_paths(
822    compiler: &mut ComponentCompilerOpaque,
823    paths: &SharedVector<SharedString>,
824) {
825    compiler
826        .as_component_compiler_mut()
827        .set_include_paths(paths.iter().map(|path| path.as_str().into()).collect())
828}
829
830#[unsafe(no_mangle)]
831pub unsafe extern "C" fn slint_interpreter_component_compiler_set_style(
832    compiler: &mut ComponentCompilerOpaque,
833    style: Slice<u8>,
834) {
835    compiler.as_component_compiler_mut().set_style(std::str::from_utf8(&style).unwrap().to_string())
836}
837
838#[unsafe(no_mangle)]
839pub unsafe extern "C" fn slint_interpreter_component_compiler_set_translation_domain(
840    compiler: &mut ComponentCompilerOpaque,
841    translation_domain: Slice<u8>,
842) {
843    compiler
844        .as_component_compiler_mut()
845        .set_translation_domain(std::str::from_utf8(&translation_domain).unwrap().to_string())
846}
847
848#[unsafe(no_mangle)]
849pub unsafe extern "C" fn slint_interpreter_component_compiler_get_style(
850    compiler: &ComponentCompilerOpaque,
851    style_out: &mut SharedString,
852) {
853    *style_out =
854        compiler.as_component_compiler().style().map_or(SharedString::default(), |s| s.into());
855}
856
857#[unsafe(no_mangle)]
858pub unsafe extern "C" fn slint_interpreter_component_compiler_get_include_paths(
859    compiler: &ComponentCompilerOpaque,
860    paths: &mut SharedVector<SharedString>,
861) {
862    paths.extend(
863        compiler
864            .as_component_compiler()
865            .include_paths()
866            .iter()
867            .map(|path| path.to_str().map_or_else(Default::default, |str| str.into())),
868    );
869}
870
871#[unsafe(no_mangle)]
872pub unsafe extern "C" fn slint_interpreter_component_compiler_get_diagnostics(
873    compiler: &ComponentCompilerOpaque,
874    out_diags: &mut SharedVector<Diagnostic>,
875) {
876    #[allow(deprecated)]
877    out_diags.extend(compiler.as_component_compiler().diagnostics().iter().map(|diagnostic| {
878        let (line, column) = diagnostic.line_column();
879        Diagnostic {
880            message: diagnostic.message().into(),
881            source_file: diagnostic
882                .source_file()
883                .and_then(|path| path.to_str())
884                .map_or_else(Default::default, |str| str.into()),
885            line,
886            column,
887            level: match diagnostic.level() {
888                i_slint_compiler::diagnostics::DiagnosticLevel::Error => DiagnosticLevel::Error,
889                i_slint_compiler::diagnostics::DiagnosticLevel::Warning => DiagnosticLevel::Warning,
890                i_slint_compiler::diagnostics::DiagnosticLevel::Note => DiagnosticLevel::Note,
891                _ => DiagnosticLevel::Warning,
892            },
893        }
894    }));
895}
896
897#[unsafe(no_mangle)]
898pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_source(
899    compiler: &mut ComponentCompilerOpaque,
900    source_code: Slice<u8>,
901    path: Slice<u8>,
902    component_definition_ptr: *mut ComponentDefinitionOpaque,
903) -> bool {
904    match spin_on::spin_on(compiler.as_component_compiler_mut().build_from_source(
905        std::str::from_utf8(&source_code).unwrap().to_string(),
906        std::str::from_utf8(&path).unwrap().to_string().into(),
907    )) {
908        Some(definition) => {
909            unsafe {
910                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
911            };
912            true
913        }
914        None => false,
915    }
916}
917
918#[unsafe(no_mangle)]
919pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_path(
920    compiler: &mut ComponentCompilerOpaque,
921    path: Slice<u8>,
922    component_definition_ptr: *mut ComponentDefinitionOpaque,
923) -> bool {
924    use std::str::FromStr;
925    match spin_on::spin_on(
926        compiler
927            .as_component_compiler_mut()
928            .build_from_path(PathBuf::from_str(std::str::from_utf8(&path).unwrap()).unwrap()),
929    ) {
930        Some(definition) => {
931            unsafe {
932                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
933            };
934            true
935        }
936        None => false,
937    }
938}
939
940/// PropertyDescriptor is a simple structure that's used to describe a property declared in .slint
941/// code. It is returned from in a vector from
942/// slint::interpreter::ComponentDefinition::properties().
943#[derive(Clone)]
944#[repr(C)]
945pub struct PropertyDescriptor {
946    /// The name of the declared property.
947    property_name: SharedString,
948    /// The type of the property.
949    property_type: ValueType,
950}
951
952#[repr(C)]
953// Note: This needs to stay the size of 1 pointer to allow for the null pointer definition
954// in the C++ wrapper to allow for the null state.
955pub struct ComponentDefinitionOpaque([usize; 1]);
956/// Asserts that ComponentCompilerOpaque is as large as ComponentCompiler and has the same alignment, to make transmute safe.
957const _: [(); std::mem::size_of::<ComponentDefinitionOpaque>()] =
958    [(); std::mem::size_of::<ComponentDefinition>()];
959const _: [(); std::mem::align_of::<ComponentDefinitionOpaque>()] =
960    [(); std::mem::align_of::<ComponentDefinition>()];
961
962impl ComponentDefinitionOpaque {
963    fn as_component_definition(&self) -> &ComponentDefinition {
964        // Safety: there should be no way to construct a ComponentDefinitionOpaque without it holding an actual ComponentDefinition
965        unsafe { std::mem::transmute::<&ComponentDefinitionOpaque, &ComponentDefinition>(self) }
966    }
967}
968
969/// Construct a new Value in the given memory location
970#[unsafe(no_mangle)]
971pub unsafe extern "C" fn slint_interpreter_component_definition_clone(
972    other: &ComponentDefinitionOpaque,
973    def: *mut ComponentDefinitionOpaque,
974) {
975    unsafe {
976        std::ptr::write(def as *mut ComponentDefinition, other.as_component_definition().clone())
977    }
978}
979
980/// Destruct the component definition in that memory location
981#[unsafe(no_mangle)]
982pub unsafe extern "C" fn slint_interpreter_component_definition_destructor(
983    val: *mut ComponentDefinitionOpaque,
984) {
985    drop(unsafe { std::ptr::read(val as *mut ComponentDefinition) })
986}
987
988/// Returns the list of properties of the component the component definition describes
989#[unsafe(no_mangle)]
990pub unsafe extern "C" fn slint_interpreter_component_definition_properties(
991    def: &ComponentDefinitionOpaque,
992    props: &mut SharedVector<PropertyDescriptor>,
993) {
994    props.extend(def.as_component_definition().properties().map(
995        |(property_name, property_type)| PropertyDescriptor {
996            property_name: property_name.into(),
997            property_type,
998        },
999    ))
1000}
1001
1002/// Returns the list of callback names of the component the component definition describes
1003#[unsafe(no_mangle)]
1004pub unsafe extern "C" fn slint_interpreter_component_definition_callbacks(
1005    def: &ComponentDefinitionOpaque,
1006    callbacks: &mut SharedVector<SharedString>,
1007) {
1008    callbacks.extend(def.as_component_definition().callbacks().map(|name| name.into()))
1009}
1010
1011/// Returns the list of function names of the component the component definition describes
1012#[unsafe(no_mangle)]
1013pub unsafe extern "C" fn slint_interpreter_component_definition_functions(
1014    def: &ComponentDefinitionOpaque,
1015    functions: &mut SharedVector<SharedString>,
1016) {
1017    functions.extend(def.as_component_definition().functions().map(|name| name.into()))
1018}
1019
1020/// Return the name of the component definition
1021#[unsafe(no_mangle)]
1022pub unsafe extern "C" fn slint_interpreter_component_definition_name(
1023    def: &ComponentDefinitionOpaque,
1024    name: &mut SharedString,
1025) {
1026    *name = def.as_component_definition().name().into()
1027}
1028
1029/// Returns a vector of strings with the names of all exported global singletons.
1030#[unsafe(no_mangle)]
1031pub unsafe extern "C" fn slint_interpreter_component_definition_globals(
1032    def: &ComponentDefinitionOpaque,
1033    names: &mut SharedVector<SharedString>,
1034) {
1035    names.extend(def.as_component_definition().globals().map(|name| name.into()))
1036}
1037
1038/// Returns a vector of the property descriptors of the properties of the specified publicly exported global
1039/// singleton. Returns true if a global exists under the specified name; false otherwise.
1040#[unsafe(no_mangle)]
1041pub unsafe extern "C" fn slint_interpreter_component_definition_global_properties(
1042    def: &ComponentDefinitionOpaque,
1043    global_name: Slice<u8>,
1044    properties: &mut SharedVector<PropertyDescriptor>,
1045) -> bool {
1046    if let Some(property_it) =
1047        def.as_component_definition().global_properties(std::str::from_utf8(&global_name).unwrap())
1048    {
1049        properties.extend(property_it.map(|(property_name, property_type)| PropertyDescriptor {
1050            property_name: property_name.into(),
1051            property_type,
1052        }));
1053        true
1054    } else {
1055        false
1056    }
1057}
1058
1059/// Returns a vector of the names of the callbacks of the specified publicly exported global
1060/// singleton. Returns true if a global exists under the specified name; false otherwise.
1061#[unsafe(no_mangle)]
1062pub unsafe extern "C" fn slint_interpreter_component_definition_global_callbacks(
1063    def: &ComponentDefinitionOpaque,
1064    global_name: Slice<u8>,
1065    names: &mut SharedVector<SharedString>,
1066) -> bool {
1067    if let Some(name_it) =
1068        def.as_component_definition().global_callbacks(std::str::from_utf8(&global_name).unwrap())
1069    {
1070        names.extend(name_it.map(|name| name.into()));
1071        true
1072    } else {
1073        false
1074    }
1075}
1076
1077/// Returns a vector of the names of the functions of the specified publicly exported global
1078/// singleton. Returns true if a global exists under the specified name; false otherwise.
1079#[unsafe(no_mangle)]
1080pub unsafe extern "C" fn slint_interpreter_component_definition_global_functions(
1081    def: &ComponentDefinitionOpaque,
1082    global_name: Slice<u8>,
1083    names: &mut SharedVector<SharedString>,
1084) -> bool {
1085    if let Some(name_it) =
1086        def.as_component_definition().global_functions(std::str::from_utf8(&global_name).unwrap())
1087    {
1088        names.extend(name_it.map(|name| name.into()));
1089        true
1090    } else {
1091        false
1092    }
1093}