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, SharedVectorModel};
10use i_slint_core::slice::Slice;
11use i_slint_core::window::WindowAdapter;
12use std::ffi::c_void;
13use vtable::VRef;
14
15/// Construct a new Value in the given memory location
16#[unsafe(no_mangle)]
17pub extern "C" fn slint_interpreter_value_new() -> Box<Value> {
18    Box::new(Value::default())
19}
20
21/// Construct a new Value in the given memory location
22#[unsafe(no_mangle)]
23pub extern "C" fn slint_interpreter_value_clone(other: &Value) -> Box<Value> {
24    Box::new(other.clone())
25}
26
27/// Destruct the value in that memory location
28#[unsafe(no_mangle)]
29pub extern "C" fn slint_interpreter_value_destructor(val: Box<Value>) {
30    drop(val);
31}
32
33#[unsafe(no_mangle)]
34pub extern "C" fn slint_interpreter_value_eq(a: &Value, b: &Value) -> bool {
35    a == b
36}
37
38/// Construct a new Value in the given memory location as string
39#[unsafe(no_mangle)]
40pub extern "C" fn slint_interpreter_value_new_string(str: &SharedString) -> Box<Value> {
41    Box::new(Value::String(str.clone()))
42}
43
44/// Construct a new Value in the given memory location as double
45#[unsafe(no_mangle)]
46pub extern "C" fn slint_interpreter_value_new_double(double: f64) -> Box<Value> {
47    Box::new(Value::Number(double))
48}
49
50/// Construct a new Value in the given memory location as bool
51#[unsafe(no_mangle)]
52pub extern "C" fn slint_interpreter_value_new_bool(b: bool) -> Box<Value> {
53    Box::new(Value::Bool(b))
54}
55
56/// Construct a new Value in the given memory location as array model
57#[unsafe(no_mangle)]
58pub extern "C" fn slint_interpreter_value_new_array_model(
59    a: &SharedVector<Box<Value>>,
60) -> Box<Value> {
61    let vec = a.iter().map(|vb| vb.as_ref().clone()).collect::<SharedVector<_>>();
62    Box::new(Value::Model(ModelRc::new(SharedVectorModel::from(vec))))
63}
64
65/// Construct a new Value in the given memory location as Brush
66#[unsafe(no_mangle)]
67pub extern "C" fn slint_interpreter_value_new_brush(brush: &Brush) -> Box<Value> {
68    Box::new(Value::Brush(brush.clone()))
69}
70
71/// Construct a new Value in the given memory location as Struct
72#[unsafe(no_mangle)]
73pub extern "C" fn slint_interpreter_value_new_struct(struc: &StructOpaque) -> Box<Value> {
74    Box::new(Value::Struct(struc.as_struct().clone()))
75}
76
77/// Construct a new Value in the given memory location as image
78#[unsafe(no_mangle)]
79pub extern "C" fn slint_interpreter_value_new_image(img: &Image) -> Box<Value> {
80    Box::new(Value::Image(img.clone()))
81}
82
83/// Construct a new Value containing a model in the given memory location
84#[unsafe(no_mangle)]
85pub unsafe extern "C" fn slint_interpreter_value_new_model(
86    model: NonNull<u8>,
87    vtable: &ModelAdaptorVTable,
88) -> Box<Value> {
89    Box::new(Value::Model(ModelRc::new(ModelAdaptorWrapper(unsafe {
90        vtable::VBox::from_raw(NonNull::from(vtable), model)
91    }))))
92}
93
94/// If the value contains a model set from [`slint_interpreter_value_new_model]` with the same vtable pointer,
95/// return the model that was set.
96/// Returns a null ptr otherwise
97#[unsafe(no_mangle)]
98pub extern "C" fn slint_interpreter_value_to_model(
99    val: &Value,
100    vtable: &ModelAdaptorVTable,
101) -> *const u8 {
102    if let Value::Model(m) = val
103        && let Some(m) = m.as_any().downcast_ref::<ModelAdaptorWrapper>()
104        && core::ptr::eq(m.0.get_vtable() as *const _, vtable as *const _)
105    {
106        return m.0.as_ptr();
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)]
144#[allow(clippy::borrowed_box)]
145pub extern "C" fn slint_interpreter_value_to_array(
146    val: &Box<Value>,
147    out: &mut SharedVector<Box<Value>>,
148) -> bool {
149    match val.as_ref() {
150        Value::Model(m) => {
151            let vec = m.iter().map(Box::new).collect::<SharedVector<_>>();
152            *out = vec;
153            true
154        }
155        _ => false,
156    }
157}
158
159#[unsafe(no_mangle)]
160pub extern "C" fn slint_interpreter_value_to_brush(val: &Value) -> Option<&Brush> {
161    match val {
162        Value::Brush(b) => Some(b),
163        _ => None,
164    }
165}
166
167#[unsafe(no_mangle)]
168pub extern "C" fn slint_interpreter_value_to_struct(val: &Value) -> *const StructOpaque {
169    match val {
170        Value::Struct(s) => s as *const Struct as *const StructOpaque,
171        _ => std::ptr::null(),
172    }
173}
174
175#[unsafe(no_mangle)]
176pub extern "C" fn slint_interpreter_value_to_image(val: &Value) -> Option<&Image> {
177    match val {
178        Value::Image(img) => Some(img),
179        _ => None,
180    }
181}
182
183#[unsafe(no_mangle)]
184pub extern "C" fn slint_interpreter_value_enum_to_string(
185    val: &Value,
186    result: &mut SharedString,
187) -> bool {
188    match val {
189        Value::EnumerationValue(_, value) => {
190            *result = SharedString::from(value);
191            true
192        }
193        _ => false,
194    }
195}
196
197#[unsafe(no_mangle)]
198pub extern "C" fn slint_interpreter_value_new_enum(
199    name: Slice<u8>,
200    value: Slice<u8>,
201) -> Box<Value> {
202    Box::new(Value::EnumerationValue(
203        std::str::from_utf8(&name).unwrap().to_string(),
204        std::str::from_utf8(&value).unwrap().to_string(),
205    ))
206}
207
208#[repr(C)]
209#[cfg(target_pointer_width = "64")]
210pub struct StructOpaque([usize; 6]);
211#[repr(C)]
212#[cfg(target_pointer_width = "32")]
213pub struct StructOpaque([u64; 4]);
214const _: [(); std::mem::size_of::<StructOpaque>()] = [(); std::mem::size_of::<Struct>()];
215const _: [(); std::mem::align_of::<StructOpaque>()] = [(); std::mem::align_of::<Struct>()];
216
217impl StructOpaque {
218    fn as_struct(&self) -> &Struct {
219        // Safety: there should be no way to construct a StructOpaque without it holding an actual Struct
220        unsafe { std::mem::transmute::<&StructOpaque, &Struct>(self) }
221    }
222    fn as_struct_mut(&mut self) -> &mut Struct {
223        // Safety: there should be no way to construct a StructOpaque without it holding an actual Struct
224        unsafe { std::mem::transmute::<&mut StructOpaque, &mut Struct>(self) }
225    }
226}
227
228/// Construct a new Struct in the given memory location
229#[unsafe(no_mangle)]
230pub unsafe extern "C" fn slint_interpreter_struct_new(val: *mut StructOpaque) {
231    unsafe { std::ptr::write(val as *mut Struct, Struct::default()) }
232}
233
234/// Construct a new Struct in the given memory location
235#[unsafe(no_mangle)]
236pub unsafe extern "C" fn slint_interpreter_struct_clone(
237    other: &StructOpaque,
238    val: *mut StructOpaque,
239) {
240    unsafe { std::ptr::write(val as *mut Struct, other.as_struct().clone()) }
241}
242
243/// Destruct the struct in that memory location
244#[unsafe(no_mangle)]
245pub unsafe extern "C" fn slint_interpreter_struct_destructor(val: *mut StructOpaque) {
246    drop(unsafe { std::ptr::read(val as *mut Struct) })
247}
248
249#[unsafe(no_mangle)]
250pub extern "C" fn slint_interpreter_struct_get_field(
251    stru: &StructOpaque,
252    name: Slice<u8>,
253) -> *mut Value {
254    if let Some(value) = stru.as_struct().get_field(std::str::from_utf8(&name).unwrap()) {
255        Box::into_raw(Box::new(value.clone()))
256    } else {
257        std::ptr::null_mut()
258    }
259}
260
261#[unsafe(no_mangle)]
262pub extern "C" fn slint_interpreter_struct_set_field(
263    stru: &mut StructOpaque,
264    name: Slice<u8>,
265    value: &Value,
266) {
267    stru.as_struct_mut().set_field(std::str::from_utf8(&name).unwrap().into(), value.clone())
268}
269
270type StructIterator<'a> = std::collections::hash_map::Iter<'a, SmolStr, Value>;
271#[repr(C)]
272pub struct StructIteratorOpaque<'a>([usize; 5], std::marker::PhantomData<StructIterator<'a>>);
273const _: [(); std::mem::size_of::<StructIteratorOpaque>()] =
274    [(); std::mem::size_of::<StructIterator>()];
275const _: [(); std::mem::align_of::<StructIteratorOpaque>()] =
276    [(); std::mem::align_of::<StructIterator>()];
277
278#[unsafe(no_mangle)]
279pub unsafe extern "C" fn slint_interpreter_struct_iterator_destructor(
280    val: *mut StructIteratorOpaque,
281) {
282    #[allow(clippy::drop_non_drop)] // the drop is a no-op but we still want to be explicit
283    drop(unsafe { std::ptr::read(val as *mut StructIterator) })
284}
285
286/// Advance the iterator and return the next value, or a null pointer
287#[unsafe(no_mangle)]
288pub unsafe extern "C" fn slint_interpreter_struct_iterator_next<'a>(
289    iter: &'a mut StructIteratorOpaque,
290    k: &mut Slice<'a, u8>,
291) -> *mut Value {
292    if let Some((str, val)) =
293        unsafe { (*(iter as *mut StructIteratorOpaque as *mut StructIterator)).next() }
294    {
295        *k = Slice::from_slice(str.as_bytes());
296        Box::into_raw(Box::new(val.clone()))
297    } else {
298        *k = Slice::default();
299        std::ptr::null_mut()
300    }
301}
302
303#[unsafe(no_mangle)]
304pub extern "C" fn slint_interpreter_struct_make_iter(
305    stru: &StructOpaque,
306) -> StructIteratorOpaque<'_> {
307    let ret_it: StructIterator = stru.as_struct().0.iter();
308    unsafe {
309        let mut r = std::mem::MaybeUninit::<StructIteratorOpaque>::uninit();
310        std::ptr::write(r.as_mut_ptr() as *mut StructIterator, ret_it);
311        r.assume_init()
312    }
313}
314
315/// Get a property. Returns a null pointer if the property does not exist.
316#[unsafe(no_mangle)]
317pub extern "C" fn slint_interpreter_component_instance_get_property(
318    inst: &ErasedItemTreeBox,
319    name: Slice<u8>,
320) -> *mut Value {
321    generativity::make_guard!(guard);
322    let comp = inst.unerase(guard);
323    match comp
324        .description()
325        .get_property(comp.borrow(), &normalize_identifier(std::str::from_utf8(&name).unwrap()))
326    {
327        Ok(val) => Box::into_raw(Box::new(val)),
328        Err(_) => std::ptr::null_mut(),
329    }
330}
331
332#[unsafe(no_mangle)]
333pub extern "C" fn slint_interpreter_component_instance_set_property(
334    inst: &ErasedItemTreeBox,
335    name: Slice<u8>,
336    val: &Value,
337) -> bool {
338    generativity::make_guard!(guard);
339    let comp = inst.unerase(guard);
340    comp.description()
341        .set_property(
342            comp.borrow(),
343            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
344            val.clone(),
345        )
346        .is_ok()
347}
348
349/// Invoke a callback or function. Returns raw boxed value on success and null ptr on failure.
350#[unsafe(no_mangle)]
351pub extern "C" fn slint_interpreter_component_instance_invoke(
352    inst: &ErasedItemTreeBox,
353    name: Slice<u8>,
354    args: Slice<Box<Value>>,
355) -> *mut Value {
356    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
357    generativity::make_guard!(guard);
358    let comp = inst.unerase(guard);
359    match comp.description().invoke(
360        comp.borrow(),
361        &normalize_identifier(std::str::from_utf8(&name).unwrap()),
362        args.as_slice(),
363    ) {
364        Ok(val) => Box::into_raw(Box::new(val)),
365        Err(_) => std::ptr::null_mut(),
366    }
367}
368
369/// Wrap the user_data provided by the native code and call the drop function on Drop.
370///
371/// Safety: user_data must be a pointer that can be destroyed by the drop_user_data function.
372/// callback must be a valid callback that initialize the `ret`
373pub struct CallbackUserData {
374    user_data: *mut c_void,
375    drop_user_data: Option<extern "C" fn(*mut c_void)>,
376    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
377}
378
379impl Drop for CallbackUserData {
380    fn drop(&mut self) {
381        if let Some(x) = self.drop_user_data {
382            x(self.user_data)
383        }
384    }
385}
386
387impl CallbackUserData {
388    pub unsafe fn new(
389        user_data: *mut c_void,
390        drop_user_data: Option<extern "C" fn(*mut c_void)>,
391        callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
392    ) -> Self {
393        Self { user_data, drop_user_data, callback }
394    }
395
396    pub fn call(&self, args: &[Value]) -> Value {
397        let args = args.iter().map(|v| v.clone().into()).collect::<Vec<_>>();
398        (self.callback)(self.user_data, Slice::from_slice(args.as_ref())).as_ref().clone()
399    }
400}
401
402/// Set a handler for the callback.
403/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
404#[unsafe(no_mangle)]
405pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
406    inst: &ErasedItemTreeBox,
407    name: Slice<u8>,
408    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
409    user_data: *mut c_void,
410    drop_user_data: Option<extern "C" fn(*mut c_void)>,
411) -> bool {
412    let ud = unsafe { CallbackUserData::new(user_data, drop_user_data, callback) };
413
414    generativity::make_guard!(guard);
415    let comp = inst.unerase(guard);
416    comp.description()
417        .set_callback_handler(
418            comp.borrow(),
419            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
420            Box::new(move |args| ud.call(args)),
421        )
422        .is_ok()
423}
424
425/// Get a global property. Returns a raw boxed value on success; nullptr otherwise.
426#[unsafe(no_mangle)]
427pub unsafe extern "C" fn slint_interpreter_component_instance_get_global_property(
428    inst: &ErasedItemTreeBox,
429    global: Slice<u8>,
430    property_name: Slice<u8>,
431) -> *mut Value {
432    generativity::make_guard!(guard);
433    let comp = inst.unerase(guard);
434    match comp
435        .description()
436        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
437        .and_then(|g| {
438            g.as_ref()
439                .get_property(&normalize_identifier(std::str::from_utf8(&property_name).unwrap()))
440        }) {
441        Ok(val) => Box::into_raw(Box::new(val)),
442        Err(_) => std::ptr::null_mut(),
443    }
444}
445
446#[unsafe(no_mangle)]
447pub extern "C" fn slint_interpreter_component_instance_set_global_property(
448    inst: &ErasedItemTreeBox,
449    global: Slice<u8>,
450    property_name: Slice<u8>,
451    val: &Value,
452) -> bool {
453    generativity::make_guard!(guard);
454    let comp = inst.unerase(guard);
455    comp.description()
456        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
457        .and_then(|g| {
458            g.as_ref()
459                .set_property(
460                    &normalize_identifier(std::str::from_utf8(&property_name).unwrap()),
461                    val.clone(),
462                )
463                .map_err(|_| ())
464        })
465        .is_ok()
466}
467
468/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
469#[unsafe(no_mangle)]
470pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callback(
471    inst: &ErasedItemTreeBox,
472    global: Slice<u8>,
473    name: Slice<u8>,
474    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
475    user_data: *mut c_void,
476    drop_user_data: Option<extern "C" fn(*mut c_void)>,
477) -> bool {
478    let ud = unsafe { CallbackUserData::new(user_data, drop_user_data, callback) };
479
480    generativity::make_guard!(guard);
481    let comp = inst.unerase(guard);
482    comp.description()
483        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
484        .and_then(|g| {
485            g.as_ref().set_callback_handler(
486                &normalize_identifier(std::str::from_utf8(&name).unwrap()),
487                Box::new(move |args| ud.call(args)),
488            )
489        })
490        .is_ok()
491}
492
493/// Invoke a global callback or function. Returns raw boxed value on success; nullptr otherwise.
494#[unsafe(no_mangle)]
495pub unsafe extern "C" fn slint_interpreter_component_instance_invoke_global(
496    inst: &ErasedItemTreeBox,
497    global: Slice<u8>,
498    callable_name: Slice<u8>,
499    args: Slice<Box<Value>>,
500) -> *mut Value {
501    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
502    generativity::make_guard!(guard);
503    let comp = inst.unerase(guard);
504    let callable_name = std::str::from_utf8(&callable_name).unwrap();
505    match comp
506        .description()
507        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
508        .and_then(|g| {
509            if matches!(
510                comp.description()
511                    .original
512                    .root_element
513                    .borrow()
514                    .lookup_property(callable_name)
515                    .property_type,
516                i_slint_compiler::langtype::Type::Function { .. }
517            ) {
518                g.as_ref()
519                    .eval_function(&normalize_identifier(callable_name), args.as_slice().to_vec())
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    /// The diagnostic is a note
693    Note,
694}
695
696/// Diagnostic describes the aspects of either a warning or an error, along
697/// with its location and a description. Diagnostics are typically returned by
698/// slint::interpreter::ComponentCompiler::diagnostics() in a vector.
699#[derive(Clone)]
700#[repr(C)]
701pub struct Diagnostic {
702    /// The message describing the warning or error.
703    message: SharedString,
704    /// The path to the source file where the warning or error is located.
705    source_file: SharedString,
706    /// The line within the source file. Line numbers start at 1.
707    line: usize,
708    /// The column within the source file. Column numbers start at 1.
709    column: usize,
710    /// The level of the diagnostic, such as a warning or an error.
711    level: DiagnosticLevel,
712}
713
714#[repr(transparent)]
715pub struct ComponentCompilerOpaque(#[allow(deprecated)] NonNull<ComponentCompiler>);
716
717#[allow(deprecated)]
718impl ComponentCompilerOpaque {
719    fn as_component_compiler(&self) -> &ComponentCompiler {
720        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
721        unsafe { self.0.as_ref() }
722    }
723    fn as_component_compiler_mut(&mut self) -> &mut ComponentCompiler {
724        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
725        unsafe { self.0.as_mut() }
726    }
727}
728
729#[unsafe(no_mangle)]
730#[allow(deprecated)]
731pub unsafe extern "C" fn slint_interpreter_component_compiler_new(
732    compiler: *mut ComponentCompilerOpaque,
733) {
734    unsafe {
735        *compiler = ComponentCompilerOpaque(NonNull::new_unchecked(Box::into_raw(Box::new(
736            ComponentCompiler::default(),
737        ))));
738    }
739}
740
741#[unsafe(no_mangle)]
742pub unsafe extern "C" fn slint_interpreter_component_compiler_destructor(
743    compiler: *mut ComponentCompilerOpaque,
744) {
745    drop(unsafe { Box::from_raw((*compiler).0.as_ptr()) })
746}
747
748#[unsafe(no_mangle)]
749pub unsafe extern "C" fn slint_interpreter_component_compiler_set_include_paths(
750    compiler: &mut ComponentCompilerOpaque,
751    paths: &SharedVector<SharedString>,
752) {
753    compiler
754        .as_component_compiler_mut()
755        .set_include_paths(paths.iter().map(|path| path.as_str().into()).collect())
756}
757
758#[unsafe(no_mangle)]
759pub unsafe extern "C" fn slint_interpreter_component_compiler_set_style(
760    compiler: &mut ComponentCompilerOpaque,
761    style: Slice<u8>,
762) {
763    compiler.as_component_compiler_mut().set_style(std::str::from_utf8(&style).unwrap().to_string())
764}
765
766#[unsafe(no_mangle)]
767pub unsafe extern "C" fn slint_interpreter_component_compiler_set_translation_domain(
768    compiler: &mut ComponentCompilerOpaque,
769    translation_domain: Slice<u8>,
770) {
771    compiler
772        .as_component_compiler_mut()
773        .set_translation_domain(std::str::from_utf8(&translation_domain).unwrap().to_string())
774}
775
776#[unsafe(no_mangle)]
777pub unsafe extern "C" fn slint_interpreter_component_compiler_get_style(
778    compiler: &ComponentCompilerOpaque,
779    style_out: &mut SharedString,
780) {
781    *style_out =
782        compiler.as_component_compiler().style().map_or(SharedString::default(), |s| s.into());
783}
784
785#[unsafe(no_mangle)]
786pub unsafe extern "C" fn slint_interpreter_component_compiler_get_include_paths(
787    compiler: &ComponentCompilerOpaque,
788    paths: &mut SharedVector<SharedString>,
789) {
790    paths.extend(
791        compiler
792            .as_component_compiler()
793            .include_paths()
794            .iter()
795            .map(|path| path.to_str().map_or_else(Default::default, |str| str.into())),
796    );
797}
798
799#[unsafe(no_mangle)]
800pub unsafe extern "C" fn slint_interpreter_component_compiler_get_diagnostics(
801    compiler: &ComponentCompilerOpaque,
802    out_diags: &mut SharedVector<Diagnostic>,
803) {
804    #[allow(deprecated)]
805    out_diags.extend(compiler.as_component_compiler().diagnostics.iter().map(|diagnostic| {
806        let (line, column) = diagnostic.line_column();
807        Diagnostic {
808            message: diagnostic.message().into(),
809            source_file: diagnostic
810                .source_file()
811                .and_then(|path| path.to_str())
812                .map_or_else(Default::default, |str| str.into()),
813            line,
814            column,
815            level: match diagnostic.level() {
816                i_slint_compiler::diagnostics::DiagnosticLevel::Error => DiagnosticLevel::Error,
817                i_slint_compiler::diagnostics::DiagnosticLevel::Warning => DiagnosticLevel::Warning,
818                i_slint_compiler::diagnostics::DiagnosticLevel::Note => DiagnosticLevel::Note,
819                _ => DiagnosticLevel::Warning,
820            },
821        }
822    }));
823}
824
825#[unsafe(no_mangle)]
826pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_source(
827    compiler: &mut ComponentCompilerOpaque,
828    source_code: Slice<u8>,
829    path: Slice<u8>,
830    component_definition_ptr: *mut ComponentDefinitionOpaque,
831) -> bool {
832    match spin_on::spin_on(compiler.as_component_compiler_mut().build_from_source(
833        std::str::from_utf8(&source_code).unwrap().to_string(),
834        std::str::from_utf8(&path).unwrap().to_string().into(),
835    )) {
836        Some(definition) => {
837            unsafe {
838                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
839            };
840            true
841        }
842        None => false,
843    }
844}
845
846#[unsafe(no_mangle)]
847pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_path(
848    compiler: &mut ComponentCompilerOpaque,
849    path: Slice<u8>,
850    component_definition_ptr: *mut ComponentDefinitionOpaque,
851) -> bool {
852    use std::str::FromStr;
853    match spin_on::spin_on(
854        compiler
855            .as_component_compiler_mut()
856            .build_from_path(PathBuf::from_str(std::str::from_utf8(&path).unwrap()).unwrap()),
857    ) {
858        Some(definition) => {
859            unsafe {
860                std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition)
861            };
862            true
863        }
864        None => false,
865    }
866}
867
868/// PropertyDescriptor is a simple structure that's used to describe a property declared in .slint
869/// code. It is returned from in a vector from
870/// slint::interpreter::ComponentDefinition::properties().
871#[derive(Clone)]
872#[repr(C)]
873pub struct PropertyDescriptor {
874    /// The name of the declared property.
875    property_name: SharedString,
876    /// The type of the property.
877    property_type: ValueType,
878}
879
880#[repr(C)]
881// Note: This needs to stay the size of 1 pointer to allow for the null pointer definition
882// in the C++ wrapper to allow for the null state.
883pub struct ComponentDefinitionOpaque([usize; 1]);
884/// Asserts that ComponentCompilerOpaque is as large as ComponentCompiler and has the same alignment, to make transmute safe.
885const _: [(); std::mem::size_of::<ComponentDefinitionOpaque>()] =
886    [(); std::mem::size_of::<ComponentDefinition>()];
887const _: [(); std::mem::align_of::<ComponentDefinitionOpaque>()] =
888    [(); std::mem::align_of::<ComponentDefinition>()];
889
890impl ComponentDefinitionOpaque {
891    fn as_component_definition(&self) -> &ComponentDefinition {
892        // Safety: there should be no way to construct a ComponentDefinitionOpaque without it holding an actual ComponentDefinition
893        unsafe { std::mem::transmute::<&ComponentDefinitionOpaque, &ComponentDefinition>(self) }
894    }
895}
896
897/// Construct a new Value in the given memory location
898#[unsafe(no_mangle)]
899pub unsafe extern "C" fn slint_interpreter_component_definition_clone(
900    other: &ComponentDefinitionOpaque,
901    def: *mut ComponentDefinitionOpaque,
902) {
903    unsafe {
904        std::ptr::write(def as *mut ComponentDefinition, other.as_component_definition().clone())
905    }
906}
907
908/// Destruct the component definition in that memory location
909#[unsafe(no_mangle)]
910pub unsafe extern "C" fn slint_interpreter_component_definition_destructor(
911    val: *mut ComponentDefinitionOpaque,
912) {
913    drop(unsafe { std::ptr::read(val as *mut ComponentDefinition) })
914}
915
916/// Returns the list of properties of the component the component definition describes
917#[unsafe(no_mangle)]
918pub unsafe extern "C" fn slint_interpreter_component_definition_properties(
919    def: &ComponentDefinitionOpaque,
920    props: &mut SharedVector<PropertyDescriptor>,
921) {
922    props.extend(def.as_component_definition().properties().map(
923        |(property_name, property_type)| PropertyDescriptor {
924            property_name: property_name.into(),
925            property_type,
926        },
927    ))
928}
929
930/// Returns the list of callback names of the component the component definition describes
931#[unsafe(no_mangle)]
932pub unsafe extern "C" fn slint_interpreter_component_definition_callbacks(
933    def: &ComponentDefinitionOpaque,
934    callbacks: &mut SharedVector<SharedString>,
935) {
936    callbacks.extend(def.as_component_definition().callbacks().map(|name| name.into()))
937}
938
939/// Returns the list of function names of the component the component definition describes
940#[unsafe(no_mangle)]
941pub unsafe extern "C" fn slint_interpreter_component_definition_functions(
942    def: &ComponentDefinitionOpaque,
943    functions: &mut SharedVector<SharedString>,
944) {
945    functions.extend(def.as_component_definition().functions().map(|name| name.into()))
946}
947
948/// Return the name of the component definition
949#[unsafe(no_mangle)]
950pub unsafe extern "C" fn slint_interpreter_component_definition_name(
951    def: &ComponentDefinitionOpaque,
952    name: &mut SharedString,
953) {
954    *name = def.as_component_definition().name().into()
955}
956
957/// Returns a vector of strings with the names of all exported global singletons.
958#[unsafe(no_mangle)]
959pub unsafe extern "C" fn slint_interpreter_component_definition_globals(
960    def: &ComponentDefinitionOpaque,
961    names: &mut SharedVector<SharedString>,
962) {
963    names.extend(def.as_component_definition().globals().map(|name| name.into()))
964}
965
966/// Returns a vector of the property descriptors of the properties of the specified publicly exported global
967/// singleton. Returns true if a global exists under the specified name; false otherwise.
968#[unsafe(no_mangle)]
969pub unsafe extern "C" fn slint_interpreter_component_definition_global_properties(
970    def: &ComponentDefinitionOpaque,
971    global_name: Slice<u8>,
972    properties: &mut SharedVector<PropertyDescriptor>,
973) -> bool {
974    if let Some(property_it) =
975        def.as_component_definition().global_properties(std::str::from_utf8(&global_name).unwrap())
976    {
977        properties.extend(property_it.map(|(property_name, property_type)| PropertyDescriptor {
978            property_name: property_name.into(),
979            property_type,
980        }));
981        true
982    } else {
983        false
984    }
985}
986
987/// Returns a vector of the names of the callbacks of the specified publicly exported global
988/// singleton. Returns true if a global exists under the specified name; false otherwise.
989#[unsafe(no_mangle)]
990pub unsafe extern "C" fn slint_interpreter_component_definition_global_callbacks(
991    def: &ComponentDefinitionOpaque,
992    global_name: Slice<u8>,
993    names: &mut SharedVector<SharedString>,
994) -> bool {
995    if let Some(name_it) =
996        def.as_component_definition().global_callbacks(std::str::from_utf8(&global_name).unwrap())
997    {
998        names.extend(name_it.map(|name| name.into()));
999        true
1000    } else {
1001        false
1002    }
1003}
1004
1005/// Returns a vector of the names of the functions of the specified publicly exported global
1006/// singleton. Returns true if a global exists under the specified name; false otherwise.
1007#[unsafe(no_mangle)]
1008pub unsafe extern "C" fn slint_interpreter_component_definition_global_functions(
1009    def: &ComponentDefinitionOpaque,
1010    global_name: Slice<u8>,
1011    names: &mut SharedVector<SharedString>,
1012) -> bool {
1013    if let Some(name_it) =
1014        def.as_component_definition().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}