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        && let Some(m) = m.as_any().downcast_ref::<ModelAdaptorWrapper>()
103        && core::ptr::eq(m.0.get_vtable() as *const _, vtable as *const _)
104    {
105        return m.0.as_ptr();
106    }
107    core::ptr::null()
108}
109
110#[unsafe(no_mangle)]
111pub extern "C" fn slint_interpreter_value_type(val: &Value) -> ValueType {
112    val.value_type()
113}
114
115#[unsafe(no_mangle)]
116pub extern "C" fn slint_interpreter_value_to_string(val: &Value) -> Option<&SharedString> {
117    match val {
118        Value::String(v) => Some(v),
119        _ => None,
120    }
121}
122
123#[unsafe(no_mangle)]
124pub extern "C" fn slint_interpreter_value_to_number(val: &Value) -> Option<&f64> {
125    match val {
126        Value::Number(v) => Some(v),
127        _ => None,
128    }
129}
130
131#[unsafe(no_mangle)]
132pub extern "C" fn slint_interpreter_value_to_bool(val: &Value) -> Option<&bool> {
133    match val {
134        Value::Bool(v) => Some(v),
135        _ => None,
136    }
137}
138
139/// Extracts a `SharedVector<ValueOpaque>` out of the given value `val`, writes that into the
140/// `out` parameter and returns true; returns false if the value does not hold an extractable
141/// array.
142#[unsafe(no_mangle)]
143#[allow(clippy::borrowed_box)]
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(Box::new).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(
262    stru: &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    #[allow(clippy::drop_non_drop)] // the drop is a no-op but we still want to be explicit
282    drop(unsafe { std::ptr::read(val as *mut StructIterator) })
283}
284
285/// Advance the iterator and return the next value, or a null pointer
286#[unsafe(no_mangle)]
287pub unsafe extern "C" fn slint_interpreter_struct_iterator_next<'a>(
288    iter: &'a mut StructIteratorOpaque,
289    k: &mut Slice<'a, u8>,
290) -> *mut Value {
291    if let Some((str, val)) =
292        unsafe { (*(iter as *mut StructIteratorOpaque as *mut StructIterator)).next() }
293    {
294        *k = Slice::from_slice(str.as_bytes());
295        Box::into_raw(Box::new(val.clone()))
296    } else {
297        *k = Slice::default();
298        std::ptr::null_mut()
299    }
300}
301
302#[unsafe(no_mangle)]
303pub extern "C" fn slint_interpreter_struct_make_iter(
304    stru: &StructOpaque,
305) -> StructIteratorOpaque<'_> {
306    let ret_it: StructIterator = stru.as_struct().0.iter();
307    unsafe {
308        let mut r = std::mem::MaybeUninit::<StructIteratorOpaque>::uninit();
309        std::ptr::write(r.as_mut_ptr() as *mut StructIterator, ret_it);
310        r.assume_init()
311    }
312}
313
314/// Get a property. Returns a null pointer if the property does not exist.
315#[unsafe(no_mangle)]
316pub extern "C" fn slint_interpreter_component_instance_get_property(
317    inst: &ErasedItemTreeBox,
318    name: Slice<u8>,
319) -> *mut Value {
320    generativity::make_guard!(guard);
321    let comp = inst.unerase(guard);
322    match comp
323        .description()
324        .get_property(comp.borrow(), &normalize_identifier(std::str::from_utf8(&name).unwrap()))
325    {
326        Ok(val) => Box::into_raw(Box::new(val)),
327        Err(_) => std::ptr::null_mut(),
328    }
329}
330
331#[unsafe(no_mangle)]
332pub extern "C" fn slint_interpreter_component_instance_set_property(
333    inst: &ErasedItemTreeBox,
334    name: Slice<u8>,
335    val: &Value,
336) -> bool {
337    generativity::make_guard!(guard);
338    let comp = inst.unerase(guard);
339    comp.description()
340        .set_property(
341            comp.borrow(),
342            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
343            val.clone(),
344        )
345        .is_ok()
346}
347
348/// Invoke a callback or function. Returns raw boxed value on success and null ptr on failure.
349#[unsafe(no_mangle)]
350pub extern "C" fn slint_interpreter_component_instance_invoke(
351    inst: &ErasedItemTreeBox,
352    name: Slice<u8>,
353    args: Slice<Box<Value>>,
354) -> *mut Value {
355    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
356    generativity::make_guard!(guard);
357    let comp = inst.unerase(guard);
358    match comp.description().invoke(
359        comp.borrow(),
360        &normalize_identifier(std::str::from_utf8(&name).unwrap()),
361        args.as_slice(),
362    ) {
363        Ok(val) => Box::into_raw(Box::new(val)),
364        Err(_) => std::ptr::null_mut(),
365    }
366}
367
368/// Wrap the user_data provided by the native code and call the drop function on Drop.
369///
370/// Safety: user_data must be a pointer that can be destroyed by the drop_user_data function.
371/// callback must be a valid callback that initialize the `ret`
372pub struct CallbackUserData {
373    user_data: *mut c_void,
374    drop_user_data: Option<extern "C" fn(*mut c_void)>,
375    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
376}
377
378impl Drop for CallbackUserData {
379    fn drop(&mut self) {
380        if let Some(x) = self.drop_user_data {
381            x(self.user_data)
382        }
383    }
384}
385
386impl CallbackUserData {
387    pub unsafe fn new(
388        user_data: *mut c_void,
389        drop_user_data: Option<extern "C" fn(*mut c_void)>,
390        callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
391    ) -> Self {
392        Self { user_data, drop_user_data, callback }
393    }
394
395    pub fn call(&self, args: &[Value]) -> Value {
396        let args = args.iter().map(|v| v.clone().into()).collect::<Vec<_>>();
397        (self.callback)(self.user_data, Slice::from_slice(args.as_ref())).as_ref().clone()
398    }
399}
400
401/// Set a handler for the callback.
402/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
403#[unsafe(no_mangle)]
404pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
405    inst: &ErasedItemTreeBox,
406    name: Slice<u8>,
407    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
408    user_data: *mut c_void,
409    drop_user_data: Option<extern "C" fn(*mut c_void)>,
410) -> bool {
411    let ud = unsafe { CallbackUserData::new(user_data, drop_user_data, callback) };
412
413    generativity::make_guard!(guard);
414    let comp = inst.unerase(guard);
415    comp.description()
416        .set_callback_handler(
417            comp.borrow(),
418            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
419            Box::new(move |args| ud.call(args)),
420        )
421        .is_ok()
422}
423
424/// Get a global property. Returns a raw boxed value on success; nullptr otherwise.
425#[unsafe(no_mangle)]
426pub unsafe extern "C" fn slint_interpreter_component_instance_get_global_property(
427    inst: &ErasedItemTreeBox,
428    global: Slice<u8>,
429    property_name: Slice<u8>,
430) -> *mut Value {
431    generativity::make_guard!(guard);
432    let comp = inst.unerase(guard);
433    match comp
434        .description()
435        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
436        .and_then(|g| {
437            g.as_ref()
438                .get_property(&normalize_identifier(std::str::from_utf8(&property_name).unwrap()))
439        }) {
440        Ok(val) => Box::into_raw(Box::new(val)),
441        Err(_) => std::ptr::null_mut(),
442    }
443}
444
445#[unsafe(no_mangle)]
446pub extern "C" fn slint_interpreter_component_instance_set_global_property(
447    inst: &ErasedItemTreeBox,
448    global: Slice<u8>,
449    property_name: Slice<u8>,
450    val: &Value,
451) -> bool {
452    generativity::make_guard!(guard);
453    let comp = inst.unerase(guard);
454    comp.description()
455        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
456        .and_then(|g| {
457            g.as_ref()
458                .set_property(
459                    &normalize_identifier(std::str::from_utf8(&property_name).unwrap()),
460                    val.clone(),
461                )
462                .map_err(|_| ())
463        })
464        .is_ok()
465}
466
467/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
468#[unsafe(no_mangle)]
469pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callback(
470    inst: &ErasedItemTreeBox,
471    global: Slice<u8>,
472    name: Slice<u8>,
473    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
474    user_data: *mut c_void,
475    drop_user_data: Option<extern "C" fn(*mut c_void)>,
476) -> bool {
477    let ud = unsafe { CallbackUserData::new(user_data, drop_user_data, callback) };
478
479    generativity::make_guard!(guard);
480    let comp = inst.unerase(guard);
481    comp.description()
482        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
483        .and_then(|g| {
484            g.as_ref().set_callback_handler(
485                &normalize_identifier(std::str::from_utf8(&name).unwrap()),
486                Box::new(move |args| ud.call(args)),
487            )
488        })
489        .is_ok()
490}
491
492/// Invoke a global callback or function. Returns raw boxed value on success; nullptr otherwise.
493#[unsafe(no_mangle)]
494pub unsafe extern "C" fn slint_interpreter_component_instance_invoke_global(
495    inst: &ErasedItemTreeBox,
496    global: Slice<u8>,
497    callable_name: Slice<u8>,
498    args: Slice<Box<Value>>,
499) -> *mut Value {
500    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
501    generativity::make_guard!(guard);
502    let comp = inst.unerase(guard);
503    let callable_name = std::str::from_utf8(&callable_name).unwrap();
504    match comp
505        .description()
506        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
507        .and_then(|g| {
508            if matches!(
509                comp.description()
510                    .original
511                    .root_element
512                    .borrow()
513                    .lookup_property(callable_name)
514                    .property_type,
515                i_slint_compiler::langtype::Type::Function { .. }
516            ) {
517                g.as_ref()
518                    .eval_function(&normalize_identifier(callable_name), args.as_slice().to_vec())
519            } else {
520                g.as_ref().invoke_callback(&normalize_identifier(callable_name), args.as_slice())
521            }
522        }) {
523        Ok(val) => Box::into_raw(Box::new(val)),
524        Err(_) => std::ptr::null_mut(),
525    }
526}
527
528/// Show or hide
529#[unsafe(no_mangle)]
530pub extern "C" fn slint_interpreter_component_instance_show(
531    inst: &ErasedItemTreeBox,
532    is_visible: bool,
533) {
534    generativity::make_guard!(guard);
535    let comp = inst.unerase(guard);
536    match is_visible {
537        true => comp.borrow_instance().window_adapter().window().show().unwrap(),
538        false => comp.borrow_instance().window_adapter().window().hide().unwrap(),
539    }
540}
541
542/// Return a window for the component
543///
544/// The out pointer must be uninitialized and must be destroyed with
545/// slint_windowrc_drop after usage
546#[unsafe(no_mangle)]
547pub unsafe extern "C" fn slint_interpreter_component_instance_window(
548    inst: &ErasedItemTreeBox,
549    out: *mut *const i_slint_core::window::ffi::WindowAdapterRcOpaque,
550) {
551    assert_eq!(
552        core::mem::size_of::<Rc<dyn WindowAdapter>>(),
553        core::mem::size_of::<i_slint_core::window::ffi::WindowAdapterRcOpaque>()
554    );
555    unsafe {
556        core::ptr::write(
557            out as *mut *const Rc<dyn WindowAdapter>,
558            inst.window_adapter_ref().unwrap() as *const _,
559        )
560    }
561}
562
563/// Instantiate an instance from a definition.
564///
565/// The `out` must be uninitialized and is going to be initialized after the call
566/// and need to be destroyed with slint_interpreter_component_instance_destructor
567#[unsafe(no_mangle)]
568pub unsafe extern "C" fn slint_interpreter_component_instance_create(
569    def: &ComponentDefinitionOpaque,
570    out: *mut ComponentInstance,
571) {
572    unsafe { std::ptr::write(out, def.as_component_definition().create().unwrap()) }
573}
574
575#[unsafe(no_mangle)]
576pub unsafe extern "C" fn slint_interpreter_component_instance_component_definition(
577    inst: &ErasedItemTreeBox,
578    component_definition_ptr: *mut ComponentDefinitionOpaque,
579) {
580    generativity::make_guard!(guard);
581    let definition = ComponentDefinition { inner: inst.unerase(guard).description().into() };
582    unsafe { std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition) };
583}
584
585#[vtable::vtable]
586#[repr(C)]
587pub struct ModelAdaptorVTable {
588    pub row_count: extern "C" fn(VRef<ModelAdaptorVTable>) -> usize,
589    pub row_data: unsafe extern "C" fn(VRef<ModelAdaptorVTable>, row: usize) -> *mut Value,
590    pub set_row_data: extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, value: Box<Value>),
591    pub get_notify: extern "C" fn(VRef<'_, ModelAdaptorVTable>) -> &ModelNotifyOpaque,
592    pub drop: extern "C" fn(VRefMut<ModelAdaptorVTable>),
593}
594
595struct ModelAdaptorWrapper(vtable::VBox<ModelAdaptorVTable>);
596impl Model for ModelAdaptorWrapper {
597    type Data = Value;
598
599    fn row_count(&self) -> usize {
600        self.0.row_count()
601    }
602
603    fn row_data(&self, row: usize) -> Option<Value> {
604        let val_ptr = unsafe { self.0.row_data(row) };
605        if val_ptr.is_null() { None } else { Some(*unsafe { Box::from_raw(val_ptr) }) }
606    }
607
608    fn model_tracker(&self) -> &dyn i_slint_core::model::ModelTracker {
609        self.0.get_notify().as_model_notify()
610    }
611
612    fn set_row_data(&self, row: usize, data: Value) {
613        let val = Box::new(data);
614        self.0.set_row_data(row, val);
615    }
616
617    fn as_any(&self) -> &dyn core::any::Any {
618        self
619    }
620}
621
622#[repr(C)]
623#[cfg(target_pointer_width = "64")]
624pub struct ModelNotifyOpaque([usize; 8]);
625#[repr(C)]
626#[cfg(target_pointer_width = "32")]
627pub struct ModelNotifyOpaque([usize; 12]);
628/// Asserts that ModelNotifyOpaque is at least as large as ModelNotify, otherwise this would overflow
629const _: usize = std::mem::size_of::<ModelNotifyOpaque>() - std::mem::size_of::<ModelNotify>();
630const _: usize = std::mem::align_of::<ModelNotifyOpaque>() - std::mem::align_of::<ModelNotify>();
631
632impl ModelNotifyOpaque {
633    fn as_model_notify(&self) -> &ModelNotify {
634        // Safety: there should be no way to construct a ModelNotifyOpaque without it holding an actual ModelNotify
635        unsafe { std::mem::transmute::<&ModelNotifyOpaque, &ModelNotify>(self) }
636    }
637}
638
639/// Construct a new ModelNotifyNotify in the given memory region
640#[unsafe(no_mangle)]
641pub unsafe extern "C" fn slint_interpreter_model_notify_new(val: *mut ModelNotifyOpaque) {
642    unsafe { std::ptr::write(val as *mut ModelNotify, ModelNotify::default()) };
643}
644
645/// Destruct the value in that memory location
646#[unsafe(no_mangle)]
647pub unsafe extern "C" fn slint_interpreter_model_notify_destructor(val: *mut ModelNotifyOpaque) {
648    drop(unsafe { std::ptr::read(val as *mut ModelNotify) })
649}
650
651#[unsafe(no_mangle)]
652pub unsafe extern "C" fn slint_interpreter_model_notify_row_changed(
653    notify: &ModelNotifyOpaque,
654    row: usize,
655) {
656    notify.as_model_notify().row_changed(row);
657}
658
659#[unsafe(no_mangle)]
660pub unsafe extern "C" fn slint_interpreter_model_notify_row_added(
661    notify: &ModelNotifyOpaque,
662    row: usize,
663    count: usize,
664) {
665    notify.as_model_notify().row_added(row, count);
666}
667
668#[unsafe(no_mangle)]
669pub unsafe extern "C" fn slint_interpreter_model_notify_reset(notify: &ModelNotifyOpaque) {
670    notify.as_model_notify().reset();
671}
672
673#[unsafe(no_mangle)]
674pub unsafe extern "C" fn slint_interpreter_model_notify_row_removed(
675    notify: &ModelNotifyOpaque,
676    row: usize,
677    count: usize,
678) {
679    notify.as_model_notify().row_removed(row, count);
680}
681
682// FIXME: Figure out how to re-export the one from compilerlib
683/// DiagnosticLevel describes the severity of a diagnostic.
684#[derive(Clone)]
685#[repr(u8)]
686pub enum DiagnosticLevel {
687    /// The diagnostic belongs to an error.
688    Error,
689    /// The diagnostic belongs to a warning.
690    Warning,
691    /// The diagnostic is a note
692    Note,
693}
694
695/// Diagnostic describes the aspects of either a warning or an error, along
696/// with its location and a description. Diagnostics are typically returned by
697/// slint::interpreter::ComponentCompiler::diagnostics() in a vector.
698#[derive(Clone)]
699#[repr(C)]
700pub struct Diagnostic {
701    /// The message describing the warning or error.
702    message: SharedString,
703    /// The path to the source file where the warning or error is located.
704    source_file: SharedString,
705    /// The line within the source file. Line numbers start at 1.
706    line: usize,
707    /// The column within the source file. Column numbers start at 1.
708    column: usize,
709    /// The level of the diagnostic, such as a warning or an error.
710    level: DiagnosticLevel,
711}
712
713#[repr(transparent)]
714pub struct ComponentCompilerOpaque(#[allow(deprecated)] NonNull<ComponentCompiler>);
715
716#[allow(deprecated)]
717impl ComponentCompilerOpaque {
718    fn as_component_compiler(&self) -> &ComponentCompiler {
719        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
720        unsafe { self.0.as_ref() }
721    }
722    fn as_component_compiler_mut(&mut self) -> &mut ComponentCompiler {
723        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
724        unsafe { self.0.as_mut() }
725    }
726}
727
728#[unsafe(no_mangle)]
729#[allow(deprecated)]
730pub unsafe extern "C" fn slint_interpreter_component_compiler_new(
731    compiler: *mut ComponentCompilerOpaque,
732) {
733    unsafe {
734        *compiler = ComponentCompilerOpaque(NonNull::new_unchecked(Box::into_raw(Box::new(
735            ComponentCompiler::default(),
736        ))));
737    }
738}
739
740#[unsafe(no_mangle)]
741pub unsafe extern "C" fn slint_interpreter_component_compiler_destructor(
742    compiler: *mut ComponentCompilerOpaque,
743) {
744    drop(unsafe { Box::from_raw((*compiler).0.as_ptr()) })
745}
746
747#[unsafe(no_mangle)]
748pub unsafe extern "C" fn slint_interpreter_component_compiler_set_include_paths(
749    compiler: &mut ComponentCompilerOpaque,
750    paths: &SharedVector<SharedString>,
751) {
752    compiler
753        .as_component_compiler_mut()
754        .set_include_paths(paths.iter().map(|path| path.as_str().into()).collect())
755}
756
757#[unsafe(no_mangle)]
758pub unsafe extern "C" fn slint_interpreter_component_compiler_set_style(
759    compiler: &mut ComponentCompilerOpaque,
760    style: Slice<u8>,
761) {
762    compiler.as_component_compiler_mut().set_style(std::str::from_utf8(&style).unwrap().to_string())
763}
764
765#[unsafe(no_mangle)]
766pub unsafe extern "C" fn slint_interpreter_component_compiler_set_translation_domain(
767    compiler: &mut ComponentCompilerOpaque,
768    translation_domain: Slice<u8>,
769) {
770    compiler
771        .as_component_compiler_mut()
772        .set_translation_domain(std::str::from_utf8(&translation_domain).unwrap().to_string())
773}
774
775#[unsafe(no_mangle)]
776pub unsafe extern "C" fn slint_interpreter_component_compiler_get_style(
777    compiler: &ComponentCompilerOpaque,
778    style_out: &mut SharedString,
779) {
780    *style_out =
781        compiler.as_component_compiler().style().map_or(SharedString::default(), |s| s.into());
782}
783
784#[unsafe(no_mangle)]
785pub unsafe extern "C" fn slint_interpreter_component_compiler_get_include_paths(
786    compiler: &ComponentCompilerOpaque,
787    paths: &mut SharedVector<SharedString>,
788) {
789    paths.extend(
790        compiler
791            .as_component_compiler()
792            .include_paths()
793            .iter()
794            .map(|path| path.to_str().map_or_else(Default::default, |str| str.into())),
795    );
796}
797
798#[unsafe(no_mangle)]
799pub unsafe extern "C" fn slint_interpreter_component_compiler_get_diagnostics(
800    compiler: &ComponentCompilerOpaque,
801    out_diags: &mut SharedVector<Diagnostic>,
802) {
803    #[allow(deprecated)]
804    out_diags.extend(compiler.as_component_compiler().diagnostics.iter().map(|diagnostic| {
805        let (line, column) = diagnostic.line_column();
806        Diagnostic {
807            message: diagnostic.message().into(),
808            source_file: diagnostic
809                .source_file()
810                .and_then(|path| path.to_str())
811                .map_or_else(Default::default, |str| str.into()),
812            line,
813            column,
814            level: match diagnostic.level() {
815                i_slint_compiler::diagnostics::DiagnosticLevel::Error => DiagnosticLevel::Error,
816                i_slint_compiler::diagnostics::DiagnosticLevel::Warning => DiagnosticLevel::Warning,
817                i_slint_compiler::diagnostics::DiagnosticLevel::Note => DiagnosticLevel::Note,
818                _ => DiagnosticLevel::Warning,
819            },
820        }
821    }));
822}
823
824#[unsafe(no_mangle)]
825pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_source(
826    compiler: &mut ComponentCompilerOpaque,
827    source_code: Slice<u8>,
828    path: Slice<u8>,
829    component_definition_ptr: *mut ComponentDefinitionOpaque,
830) -> bool {
831    match spin_on::spin_on(compiler.as_component_compiler_mut().build_from_source(
832        std::str::from_utf8(&source_code).unwrap().to_string(),
833        std::str::from_utf8(&path).unwrap().to_string().into(),
834    )) {
835        Some(definition) => {
836            unsafe {
837                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
838            };
839            true
840        }
841        None => false,
842    }
843}
844
845#[unsafe(no_mangle)]
846pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_path(
847    compiler: &mut ComponentCompilerOpaque,
848    path: Slice<u8>,
849    component_definition_ptr: *mut ComponentDefinitionOpaque,
850) -> bool {
851    use std::str::FromStr;
852    match spin_on::spin_on(
853        compiler
854            .as_component_compiler_mut()
855            .build_from_path(PathBuf::from_str(std::str::from_utf8(&path).unwrap()).unwrap()),
856    ) {
857        Some(definition) => {
858            unsafe {
859                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
860            };
861            true
862        }
863        None => false,
864    }
865}
866
867/// PropertyDescriptor is a simple structure that's used to describe a property declared in .slint
868/// code. It is returned from in a vector from
869/// slint::interpreter::ComponentDefinition::properties().
870#[derive(Clone)]
871#[repr(C)]
872pub struct PropertyDescriptor {
873    /// The name of the declared property.
874    property_name: SharedString,
875    /// The type of the property.
876    property_type: ValueType,
877}
878
879#[repr(C)]
880// Note: This needs to stay the size of 1 pointer to allow for the null pointer definition
881// in the C++ wrapper to allow for the null state.
882pub struct ComponentDefinitionOpaque([usize; 1]);
883/// Asserts that ComponentCompilerOpaque is as large as ComponentCompiler and has the same alignment, to make transmute safe.
884const _: [(); std::mem::size_of::<ComponentDefinitionOpaque>()] =
885    [(); std::mem::size_of::<ComponentDefinition>()];
886const _: [(); std::mem::align_of::<ComponentDefinitionOpaque>()] =
887    [(); std::mem::align_of::<ComponentDefinition>()];
888
889impl ComponentDefinitionOpaque {
890    fn as_component_definition(&self) -> &ComponentDefinition {
891        // Safety: there should be no way to construct a ComponentDefinitionOpaque without it holding an actual ComponentDefinition
892        unsafe { std::mem::transmute::<&ComponentDefinitionOpaque, &ComponentDefinition>(self) }
893    }
894}
895
896/// Construct a new Value in the given memory location
897#[unsafe(no_mangle)]
898pub unsafe extern "C" fn slint_interpreter_component_definition_clone(
899    other: &ComponentDefinitionOpaque,
900    def: *mut ComponentDefinitionOpaque,
901) {
902    unsafe {
903        std::ptr::write(def as *mut ComponentDefinition, other.as_component_definition().clone())
904    }
905}
906
907/// Destruct the component definition in that memory location
908#[unsafe(no_mangle)]
909pub unsafe extern "C" fn slint_interpreter_component_definition_destructor(
910    val: *mut ComponentDefinitionOpaque,
911) {
912    drop(unsafe { std::ptr::read(val as *mut ComponentDefinition) })
913}
914
915/// Returns the list of properties of the component the component definition describes
916#[unsafe(no_mangle)]
917pub unsafe extern "C" fn slint_interpreter_component_definition_properties(
918    def: &ComponentDefinitionOpaque,
919    props: &mut SharedVector<PropertyDescriptor>,
920) {
921    props.extend(def.as_component_definition().properties().map(
922        |(property_name, property_type)| PropertyDescriptor {
923            property_name: property_name.into(),
924            property_type,
925        },
926    ))
927}
928
929/// Returns the list of callback names of the component the component definition describes
930#[unsafe(no_mangle)]
931pub unsafe extern "C" fn slint_interpreter_component_definition_callbacks(
932    def: &ComponentDefinitionOpaque,
933    callbacks: &mut SharedVector<SharedString>,
934) {
935    callbacks.extend(def.as_component_definition().callbacks().map(|name| name.into()))
936}
937
938/// Returns the list of function names of the component the component definition describes
939#[unsafe(no_mangle)]
940pub unsafe extern "C" fn slint_interpreter_component_definition_functions(
941    def: &ComponentDefinitionOpaque,
942    functions: &mut SharedVector<SharedString>,
943) {
944    functions.extend(def.as_component_definition().functions().map(|name| name.into()))
945}
946
947/// Return the name of the component definition
948#[unsafe(no_mangle)]
949pub unsafe extern "C" fn slint_interpreter_component_definition_name(
950    def: &ComponentDefinitionOpaque,
951    name: &mut SharedString,
952) {
953    *name = def.as_component_definition().name().into()
954}
955
956/// Returns a vector of strings with the names of all exported global singletons.
957#[unsafe(no_mangle)]
958pub unsafe extern "C" fn slint_interpreter_component_definition_globals(
959    def: &ComponentDefinitionOpaque,
960    names: &mut SharedVector<SharedString>,
961) {
962    names.extend(def.as_component_definition().globals().map(|name| name.into()))
963}
964
965/// Returns a vector of the property descriptors of the properties of the specified publicly exported global
966/// singleton. Returns true if a global exists under the specified name; false otherwise.
967#[unsafe(no_mangle)]
968pub unsafe extern "C" fn slint_interpreter_component_definition_global_properties(
969    def: &ComponentDefinitionOpaque,
970    global_name: Slice<u8>,
971    properties: &mut SharedVector<PropertyDescriptor>,
972) -> bool {
973    if let Some(property_it) =
974        def.as_component_definition().global_properties(std::str::from_utf8(&global_name).unwrap())
975    {
976        properties.extend(property_it.map(|(property_name, property_type)| PropertyDescriptor {
977            property_name: property_name.into(),
978            property_type,
979        }));
980        true
981    } else {
982        false
983    }
984}
985
986/// Returns a vector of the names of the callbacks of the specified publicly exported global
987/// singleton. Returns true if a global exists under the specified name; false otherwise.
988#[unsafe(no_mangle)]
989pub unsafe extern "C" fn slint_interpreter_component_definition_global_callbacks(
990    def: &ComponentDefinitionOpaque,
991    global_name: Slice<u8>,
992    names: &mut SharedVector<SharedString>,
993) -> bool {
994    if let Some(name_it) =
995        def.as_component_definition().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) =
1013        def.as_component_definition().global_functions(std::str::from_utf8(&global_name).unwrap())
1014    {
1015        names.extend(name_it.map(|name| name.into()));
1016        true
1017    } else {
1018        false
1019    }
1020}