Skip to main content

slint_interpreter/
ffi.rs

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