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 get_notify: extern "C" fn(VRef<'_, ModelAdaptorVTable>) -> &ModelNotifyOpaque,
648    pub drop: extern "C" fn(VRefMut<ModelAdaptorVTable>),
649}
650
651struct ModelAdaptorWrapper(vtable::VBox<ModelAdaptorVTable>);
652impl Model for ModelAdaptorWrapper {
653    type Data = Value;
654
655    fn row_count(&self) -> usize {
656        self.0.row_count()
657    }
658
659    fn row_data(&self, row: usize) -> Option<Value> {
660        let val_ptr = unsafe { self.0.row_data(row) };
661        if val_ptr.is_null() { None } else { Some(*unsafe { Box::from_raw(val_ptr) }) }
662    }
663
664    fn model_tracker(&self) -> &dyn i_slint_core::model::ModelTracker {
665        self.0.get_notify().as_model_notify()
666    }
667
668    fn set_row_data(&self, row: usize, data: Value) {
669        let val = Box::new(data);
670        self.0.set_row_data(row, val);
671    }
672
673    fn as_any(&self) -> &dyn core::any::Any {
674        self
675    }
676}
677
678#[repr(C)]
679#[cfg(target_pointer_width = "64")]
680pub struct ModelNotifyOpaque([usize; 8]);
681#[repr(C)]
682#[cfg(target_pointer_width = "32")]
683pub struct ModelNotifyOpaque([usize; 12]);
684/// Asserts that ModelNotifyOpaque is at least as large as ModelNotify, otherwise this would overflow
685const _: usize = std::mem::size_of::<ModelNotifyOpaque>() - std::mem::size_of::<ModelNotify>();
686const _: usize = std::mem::align_of::<ModelNotifyOpaque>() - std::mem::align_of::<ModelNotify>();
687
688impl ModelNotifyOpaque {
689    fn as_model_notify(&self) -> &ModelNotify {
690        // Safety: there should be no way to construct a ModelNotifyOpaque without it holding an actual ModelNotify
691        unsafe { std::mem::transmute::<&ModelNotifyOpaque, &ModelNotify>(self) }
692    }
693}
694
695/// Construct a new ModelNotifyNotify in the given memory region
696#[unsafe(no_mangle)]
697pub unsafe extern "C" fn slint_interpreter_model_notify_new(val: *mut ModelNotifyOpaque) {
698    unsafe { std::ptr::write(val as *mut ModelNotify, ModelNotify::default()) };
699}
700
701/// Destruct the value in that memory location
702#[unsafe(no_mangle)]
703pub unsafe extern "C" fn slint_interpreter_model_notify_destructor(val: *mut ModelNotifyOpaque) {
704    drop(unsafe { std::ptr::read(val as *mut ModelNotify) })
705}
706
707#[unsafe(no_mangle)]
708pub unsafe extern "C" fn slint_interpreter_model_notify_row_changed(
709    notify: &ModelNotifyOpaque,
710    row: usize,
711) {
712    notify.as_model_notify().row_changed(row);
713}
714
715#[unsafe(no_mangle)]
716pub unsafe extern "C" fn slint_interpreter_model_notify_row_added(
717    notify: &ModelNotifyOpaque,
718    row: usize,
719    count: usize,
720) {
721    notify.as_model_notify().row_added(row, count);
722}
723
724#[unsafe(no_mangle)]
725pub unsafe extern "C" fn slint_interpreter_model_notify_reset(notify: &ModelNotifyOpaque) {
726    notify.as_model_notify().reset();
727}
728
729#[unsafe(no_mangle)]
730pub unsafe extern "C" fn slint_interpreter_model_notify_row_removed(
731    notify: &ModelNotifyOpaque,
732    row: usize,
733    count: usize,
734) {
735    notify.as_model_notify().row_removed(row, count);
736}
737
738// FIXME: Figure out how to re-export the one from compilerlib
739/// DiagnosticLevel describes the severity of a diagnostic.
740#[derive(Clone)]
741#[repr(u8)]
742pub enum DiagnosticLevel {
743    /// The diagnostic belongs to an error.
744    Error,
745    /// The diagnostic belongs to a warning.
746    Warning,
747    /// The diagnostic is a note
748    Note,
749}
750
751/// Diagnostic describes the aspects of either a warning or an error, along
752/// with its location and a description. Diagnostics are typically returned by
753/// slint::interpreter::ComponentCompiler::diagnostics() in a vector.
754#[derive(Clone)]
755#[repr(C)]
756pub struct Diagnostic {
757    /// The message describing the warning or error.
758    message: SharedString,
759    /// The path to the source file where the warning or error is located.
760    source_file: SharedString,
761    /// The line within the source file. Line numbers start at 1.
762    line: usize,
763    /// The column within the source file. Column numbers start at 1.
764    column: usize,
765    /// The level of the diagnostic, such as a warning or an error.
766    level: DiagnosticLevel,
767}
768
769#[repr(transparent)]
770pub struct ComponentCompilerOpaque(#[allow(deprecated)] NonNull<ComponentCompiler>);
771
772#[allow(deprecated)]
773impl ComponentCompilerOpaque {
774    fn as_component_compiler(&self) -> &ComponentCompiler {
775        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
776        unsafe { self.0.as_ref() }
777    }
778    fn as_component_compiler_mut(&mut self) -> &mut ComponentCompiler {
779        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
780        unsafe { self.0.as_mut() }
781    }
782}
783
784#[unsafe(no_mangle)]
785#[allow(deprecated)]
786pub unsafe extern "C" fn slint_interpreter_component_compiler_new(
787    compiler: *mut ComponentCompilerOpaque,
788) {
789    unsafe {
790        *compiler = ComponentCompilerOpaque(NonNull::new_unchecked(Box::into_raw(Box::new(
791            ComponentCompiler::default(),
792        ))));
793    }
794}
795
796#[unsafe(no_mangle)]
797pub unsafe extern "C" fn slint_interpreter_component_compiler_destructor(
798    compiler: *mut ComponentCompilerOpaque,
799) {
800    drop(unsafe { Box::from_raw((*compiler).0.as_ptr()) })
801}
802
803#[unsafe(no_mangle)]
804pub unsafe extern "C" fn slint_interpreter_component_compiler_set_include_paths(
805    compiler: &mut ComponentCompilerOpaque,
806    paths: &SharedVector<SharedString>,
807) {
808    compiler
809        .as_component_compiler_mut()
810        .set_include_paths(paths.iter().map(|path| path.as_str().into()).collect())
811}
812
813#[unsafe(no_mangle)]
814pub unsafe extern "C" fn slint_interpreter_component_compiler_set_style(
815    compiler: &mut ComponentCompilerOpaque,
816    style: Slice<u8>,
817) {
818    compiler.as_component_compiler_mut().set_style(std::str::from_utf8(&style).unwrap().to_string())
819}
820
821#[unsafe(no_mangle)]
822pub unsafe extern "C" fn slint_interpreter_component_compiler_set_translation_domain(
823    compiler: &mut ComponentCompilerOpaque,
824    translation_domain: Slice<u8>,
825) {
826    compiler
827        .as_component_compiler_mut()
828        .set_translation_domain(std::str::from_utf8(&translation_domain).unwrap().to_string())
829}
830
831#[unsafe(no_mangle)]
832pub unsafe extern "C" fn slint_interpreter_component_compiler_get_style(
833    compiler: &ComponentCompilerOpaque,
834    style_out: &mut SharedString,
835) {
836    *style_out =
837        compiler.as_component_compiler().style().map_or(SharedString::default(), |s| s.into());
838}
839
840#[unsafe(no_mangle)]
841pub unsafe extern "C" fn slint_interpreter_component_compiler_get_include_paths(
842    compiler: &ComponentCompilerOpaque,
843    paths: &mut SharedVector<SharedString>,
844) {
845    paths.extend(
846        compiler
847            .as_component_compiler()
848            .include_paths()
849            .iter()
850            .map(|path| path.to_str().map_or_else(Default::default, |str| str.into())),
851    );
852}
853
854#[unsafe(no_mangle)]
855pub unsafe extern "C" fn slint_interpreter_component_compiler_get_diagnostics(
856    compiler: &ComponentCompilerOpaque,
857    out_diags: &mut SharedVector<Diagnostic>,
858) {
859    #[allow(deprecated)]
860    out_diags.extend(compiler.as_component_compiler().diagnostics().iter().map(|diagnostic| {
861        let (line, column) = diagnostic.line_column();
862        Diagnostic {
863            message: diagnostic.message().into(),
864            source_file: diagnostic
865                .source_file()
866                .and_then(|path| path.to_str())
867                .map_or_else(Default::default, |str| str.into()),
868            line,
869            column,
870            level: match diagnostic.level() {
871                i_slint_compiler::diagnostics::DiagnosticLevel::Error => DiagnosticLevel::Error,
872                i_slint_compiler::diagnostics::DiagnosticLevel::Warning => DiagnosticLevel::Warning,
873                i_slint_compiler::diagnostics::DiagnosticLevel::Note => DiagnosticLevel::Note,
874                _ => DiagnosticLevel::Warning,
875            },
876        }
877    }));
878}
879
880#[unsafe(no_mangle)]
881pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_source(
882    compiler: &mut ComponentCompilerOpaque,
883    source_code: Slice<u8>,
884    path: Slice<u8>,
885    component_definition_ptr: *mut ComponentDefinitionOpaque,
886) -> bool {
887    match spin_on::spin_on(compiler.as_component_compiler_mut().build_from_source(
888        std::str::from_utf8(&source_code).unwrap().to_string(),
889        std::str::from_utf8(&path).unwrap().to_string().into(),
890    )) {
891        Some(definition) => {
892            unsafe {
893                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
894            };
895            true
896        }
897        None => false,
898    }
899}
900
901#[unsafe(no_mangle)]
902pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_path(
903    compiler: &mut ComponentCompilerOpaque,
904    path: Slice<u8>,
905    component_definition_ptr: *mut ComponentDefinitionOpaque,
906) -> bool {
907    use std::str::FromStr;
908    match spin_on::spin_on(
909        compiler
910            .as_component_compiler_mut()
911            .build_from_path(PathBuf::from_str(std::str::from_utf8(&path).unwrap()).unwrap()),
912    ) {
913        Some(definition) => {
914            unsafe {
915                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
916            };
917            true
918        }
919        None => false,
920    }
921}
922
923/// PropertyDescriptor is a simple structure that's used to describe a property declared in .slint
924/// code. It is returned from in a vector from
925/// slint::interpreter::ComponentDefinition::properties().
926#[derive(Clone)]
927#[repr(C)]
928pub struct PropertyDescriptor {
929    /// The name of the declared property.
930    property_name: SharedString,
931    /// The type of the property.
932    property_type: ValueType,
933}
934
935#[repr(C)]
936// Note: This needs to stay the size of 1 pointer to allow for the null pointer definition
937// in the C++ wrapper to allow for the null state.
938pub struct ComponentDefinitionOpaque([usize; 1]);
939/// Asserts that ComponentCompilerOpaque is as large as ComponentCompiler and has the same alignment, to make transmute safe.
940const _: [(); std::mem::size_of::<ComponentDefinitionOpaque>()] =
941    [(); std::mem::size_of::<ComponentDefinition>()];
942const _: [(); std::mem::align_of::<ComponentDefinitionOpaque>()] =
943    [(); std::mem::align_of::<ComponentDefinition>()];
944
945impl ComponentDefinitionOpaque {
946    fn as_component_definition(&self) -> &ComponentDefinition {
947        // Safety: there should be no way to construct a ComponentDefinitionOpaque without it holding an actual ComponentDefinition
948        unsafe { std::mem::transmute::<&ComponentDefinitionOpaque, &ComponentDefinition>(self) }
949    }
950}
951
952/// Construct a new Value in the given memory location
953#[unsafe(no_mangle)]
954pub unsafe extern "C" fn slint_interpreter_component_definition_clone(
955    other: &ComponentDefinitionOpaque,
956    def: *mut ComponentDefinitionOpaque,
957) {
958    unsafe {
959        std::ptr::write(def as *mut ComponentDefinition, other.as_component_definition().clone())
960    }
961}
962
963/// Destruct the component definition in that memory location
964#[unsafe(no_mangle)]
965pub unsafe extern "C" fn slint_interpreter_component_definition_destructor(
966    val: *mut ComponentDefinitionOpaque,
967) {
968    drop(unsafe { std::ptr::read(val as *mut ComponentDefinition) })
969}
970
971/// Returns the list of properties of the component the component definition describes
972#[unsafe(no_mangle)]
973pub unsafe extern "C" fn slint_interpreter_component_definition_properties(
974    def: &ComponentDefinitionOpaque,
975    props: &mut SharedVector<PropertyDescriptor>,
976) {
977    props.extend(def.as_component_definition().properties().map(
978        |(property_name, property_type)| PropertyDescriptor {
979            property_name: property_name.into(),
980            property_type,
981        },
982    ))
983}
984
985/// Returns the list of callback names of the component the component definition describes
986#[unsafe(no_mangle)]
987pub unsafe extern "C" fn slint_interpreter_component_definition_callbacks(
988    def: &ComponentDefinitionOpaque,
989    callbacks: &mut SharedVector<SharedString>,
990) {
991    callbacks.extend(def.as_component_definition().callbacks().map(|name| name.into()))
992}
993
994/// Returns the list of function names of the component the component definition describes
995#[unsafe(no_mangle)]
996pub unsafe extern "C" fn slint_interpreter_component_definition_functions(
997    def: &ComponentDefinitionOpaque,
998    functions: &mut SharedVector<SharedString>,
999) {
1000    functions.extend(def.as_component_definition().functions().map(|name| name.into()))
1001}
1002
1003/// Return the name of the component definition
1004#[unsafe(no_mangle)]
1005pub unsafe extern "C" fn slint_interpreter_component_definition_name(
1006    def: &ComponentDefinitionOpaque,
1007    name: &mut SharedString,
1008) {
1009    *name = def.as_component_definition().name().into()
1010}
1011
1012/// Returns a vector of strings with the names of all exported global singletons.
1013#[unsafe(no_mangle)]
1014pub unsafe extern "C" fn slint_interpreter_component_definition_globals(
1015    def: &ComponentDefinitionOpaque,
1016    names: &mut SharedVector<SharedString>,
1017) {
1018    names.extend(def.as_component_definition().globals().map(|name| name.into()))
1019}
1020
1021/// Returns a vector of the property descriptors of the properties of the specified publicly exported global
1022/// singleton. Returns true if a global exists under the specified name; false otherwise.
1023#[unsafe(no_mangle)]
1024pub unsafe extern "C" fn slint_interpreter_component_definition_global_properties(
1025    def: &ComponentDefinitionOpaque,
1026    global_name: Slice<u8>,
1027    properties: &mut SharedVector<PropertyDescriptor>,
1028) -> bool {
1029    if let Some(property_it) =
1030        def.as_component_definition().global_properties(std::str::from_utf8(&global_name).unwrap())
1031    {
1032        properties.extend(property_it.map(|(property_name, property_type)| PropertyDescriptor {
1033            property_name: property_name.into(),
1034            property_type,
1035        }));
1036        true
1037    } else {
1038        false
1039    }
1040}
1041
1042/// Returns a vector of the names of the callbacks of the specified publicly exported global
1043/// singleton. Returns true if a global exists under the specified name; false otherwise.
1044#[unsafe(no_mangle)]
1045pub unsafe extern "C" fn slint_interpreter_component_definition_global_callbacks(
1046    def: &ComponentDefinitionOpaque,
1047    global_name: Slice<u8>,
1048    names: &mut SharedVector<SharedString>,
1049) -> bool {
1050    if let Some(name_it) =
1051        def.as_component_definition().global_callbacks(std::str::from_utf8(&global_name).unwrap())
1052    {
1053        names.extend(name_it.map(|name| name.into()));
1054        true
1055    } else {
1056        false
1057    }
1058}
1059
1060/// Returns a vector of the names of the functions of the specified publicly exported global
1061/// singleton. Returns true if a global exists under the specified name; false otherwise.
1062#[unsafe(no_mangle)]
1063pub unsafe extern "C" fn slint_interpreter_component_definition_global_functions(
1064    def: &ComponentDefinitionOpaque,
1065    global_name: Slice<u8>,
1066    names: &mut SharedVector<SharedString>,
1067) -> bool {
1068    if let Some(name_it) =
1069        def.as_component_definition().global_functions(std::str::from_utf8(&global_name).unwrap())
1070    {
1071        names.extend(name_it.map(|name| name.into()));
1072        true
1073    } else {
1074        false
1075    }
1076}