1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4use anyhow::Context;
5use std::io::{BufWriter, Write};
6use std::path::{Path, PathBuf};
7
8// cSpell: ignore compat constexpr corelib deps sharedvector pathdata
9
10fn enums(path: &Path) -> anyhow::Result<()> {
11 let mut enums_priv = BufWriter::new(
12 std::fs::File::create(path.join("slint_enums_internal.h"))
13 .context("Error creating slint_enums_internal.h file")?,
14 );
15 writeln!(enums_priv, "#pragma once")?;
16 writeln!(enums_priv, "// This file is auto-generated from {}", file!())?;
17 writeln!(enums_priv, "#include \"slint_enums.h\"")?;
18 writeln!(enums_priv, "namespace slint::cbindgen_private {{")?;
19 let mut enums_pub = BufWriter::new(
20 std::fs::File::create(path.join("slint_enums.h"))
21 .context("Error creating slint_enums.h file")?,
22 );
23 writeln!(enums_pub, "#pragma once")?;
24 writeln!(enums_pub, "// This file is auto-generated from {}", file!())?;
25 writeln!(enums_pub, "namespace slint {{")?;
26 macro_rules! enum_file {
27 (PointerEventButton) => {{
28 writeln!(enums_priv, "using slint::PointerEventButton;")?;
29 &mut enums_pub
30 }};
31 ($_:ident) => {
32 &mut enums_priv
33 };
34 }
35 macro_rules! print_enums {
36 ($( $(#[doc = $enum_doc:literal])* $(#[non_exhaustive])? enum $Name:ident { $( $(#[doc = $value_doc:literal])* $Value:ident,)* })*) => {
37 $(
38 let file = enum_file!($Name);
39 $(writeln!(file, "///{}", $enum_doc)?;)*
40 writeln!(file, "enum class {} {{", stringify!($Name))?;
41 $(
42 $(writeln!(file, " ///{}", $value_doc)?;)*
43 writeln!(file, " {},", stringify!($Value).trim_start_matches("r#"))?;
44 )*
45 writeln!(file, "}};")?;
46 )*
47 }
48 }
49 i_slint_common::for_each_enums!(print_enums);
50
51 writeln!(enums_pub, "}}")?;
52 writeln!(enums_priv, "}}")?;
53
54 // Print the key codes constants
55 // This is not an enum, but fits well in that file
56 writeln!(
57 enums_pub,
58 r#"
59/// This namespace contains constants for each special non-printable key.
60///
61/// Each constant can be converted to SharedString.
62/// The constants are meant to be used with the slint::Window::dispatch_key_press_event() and
63/// slint::Window::dispatch_key_release_event() functions.
64///
65/// Example:
66/// ```
67/// window.dispatch_key_press_event(slint::platform::key_codes::Tab);
68/// ```
69namespace slint::platform::key_codes {{
70"#
71 )?;
72 macro_rules! print_key_codes {
73 ($($char:literal # $name:ident # $($qt:ident)|* # $($winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
74 $(
75 writeln!(enums_pub, "/// A constant that represents the key code to be used in slint::Window::dispatch_key_press_event()")?;
76 writeln!(enums_pub, r#"constexpr std::u8string_view {} = u8"\u{:04x}";"#, stringify!($name), $char as u32)?;
77 )*
78 };
79 }
80 i_slint_common::for_each_special_keys!(print_key_codes);
81 writeln!(enums_pub, "}}")?;
82
83 Ok(())
84}
85
86fn builtin_structs(path: &Path) -> anyhow::Result<()> {
87 let mut structs_pub = BufWriter::new(
88 std::fs::File::create(path.join("slint_builtin_structs.h"))
89 .context("Error creating slint_builtin_structs.h file")?,
90 );
91 writeln!(structs_pub, "#pragma once")?;
92 writeln!(structs_pub, "// This file is auto-generated from {}", file!())?;
93 writeln!(structs_pub, "namespace slint {{")?;
94
95 let mut structs_priv = BufWriter::new(
96 std::fs::File::create(path.join("slint_builtin_structs_internal.h"))
97 .context("Error creating slint_builtin_structs_internal.h file")?,
98 );
99 writeln!(structs_priv, "#pragma once")?;
100 writeln!(structs_priv, "// This file is auto-generated from {}", file!())?;
101 writeln!(structs_priv, "#include \"slint_builtin_structs.h\"")?;
102 writeln!(structs_priv, "#include \"slint_enums_internal.h\"")?;
103 writeln!(structs_priv, "namespace slint::cbindgen_private {{")?;
104 writeln!(structs_priv, "enum class KeyEventType : uint8_t;")?;
105 macro_rules! struct_file {
106 (StandardListViewItem) => {{
107 writeln!(structs_priv, "using slint::StandardListViewItem;")?;
108 &mut structs_pub
109 }};
110 ($_:ident) => {
111 &mut structs_priv
112 };
113 }
114 macro_rules! print_structs {
115 ($(
116 $(#[doc = $struct_doc:literal])*
117 $(#[non_exhaustive])?
118 $(#[derive(Copy, Eq)])?
119 struct $Name:ident {
120 @name = $inner_name:literal
121 export {
122 $( $(#[doc = $pub_doc:literal])* $pub_field:ident : $pub_type:ty, )*
123 }
124 private {
125 $( $(#[doc = $pri_doc:literal])* $pri_field:ident : $pri_type:ty, )*
126 }
127 }
128 )*) => {
129 $(
130 let file = struct_file!($Name);
131 $(writeln!(file, "///{}", $struct_doc)?;)*
132 writeln!(file, "struct {} {{", stringify!($Name))?;
133 $(
134 $(writeln!(file, " ///{}", $pub_doc)?;)*
135 let pub_type = match stringify!($pub_type) {
136 "i32" => "int32_t",
137 "f32" | "Coord" => "float",
138 other => other,
139 };
140 writeln!(file, " {} {};", pub_type, stringify!($pub_field))?;
141 )*
142 $(
143 $(writeln!(file, " ///{}", $pri_doc)?;)*
144 let pri_type = match stringify!($pri_type) {
145 "usize" => "uintptr_t",
146 "crate::animations::Instant" => "uint64_t",
147 // This shouldn't be accessed by the C++ anyway, just need to have the same ABI in a struct
148 "Option<i32>" => "std::pair<int32_t, int32_t>",
149 "Option<core::ops::Range<i32>>" => "std::tuple<int32_t, int32_t, int32_t>",
150 other => other,
151 };
152 writeln!(file, " {} {};", pri_type, stringify!($pri_field))?;
153 )*
154 writeln!(file, " /// \\private")?;
155 writeln!(file, " {}", format!("friend bool operator==(const {name}&, const {name}&) = default;", name = stringify!($Name)))?;
156 writeln!(file, " /// \\private")?;
157 writeln!(file, " {}", format!("friend bool operator!=(const {name}&, const {name}&) = default;", name = stringify!($Name)))?;
158 writeln!(file, "}};")?;
159 )*
160 };
161 }
162 i_slint_common::for_each_builtin_structs!(print_structs);
163 writeln!(structs_priv, "}}")?;
164 writeln!(structs_pub, "}}")?;
165 Ok(())
166}
167
168fn ensure_cargo_rerun_for_crate(
169 crate_dir: &Path,
170 dependencies: &mut Vec<PathBuf>,
171) -> anyhow::Result<()> {
172 dependencies.push(crate_dir.to_path_buf());
173 for entry: Result in std::fs::read_dir(path:crate_dir)? {
174 let entry: DirEntry = entry?;
175 if entry.path().extension().map_or(default:false, |e: &OsStr| e == "rs") {
176 dependencies.push(entry.path());
177 }
178 }
179 Ok(())
180}
181
182fn default_config() -> cbindgen::Config {
183 let mut config = cbindgen::Config::default();
184 config.pragma_once = true;
185 config.include_version = true;
186 config.namespaces = Some(vec!["slint".into(), "cbindgen_private".into()]);
187 config.line_length = 100;
188 config.tab_width = 4;
189 // Note: we might need to switch to C if we need to generate bindings for language that needs C headers
190 config.language = cbindgen::Language::Cxx;
191 config.cpp_compat = true;
192 config.documentation = true;
193 config.export = cbindgen::ExportConfig {
194 rename: [
195 ("Callback".into(), "private_api::CallbackHelper".into()),
196 ("VoidArg".into(), "void".into()),
197 ("KeyEventArg".into(), "KeyEvent".into()),
198 ("PointerEventArg".into(), "PointerEvent".into()),
199 ("PointerScrollEventArg".into(), "PointerScrollEvent".into()),
200 ("PointArg".into(), "slint::LogicalPosition".into()),
201 ("FloatArg".into(), "float".into()),
202 ("Coord".into(), "float".into()),
203 ]
204 .iter()
205 .cloned()
206 .collect(),
207 ..Default::default()
208 };
209 config.defines = [
210 ("target_pointer_width = 64".into(), "SLINT_TARGET_64".into()),
211 ("target_pointer_width = 32".into(), "SLINT_TARGET_32".into()),
212 ("target_arch = wasm32".into(), "SLINT_TARGET_WASM".into()), // Disable any wasm guarded code in C++, too - so that there are no gaps in enums.
213 ]
214 .iter()
215 .cloned()
216 .collect();
217 config
218}
219
220fn gen_item_declarations(items: &[&str]) -> String {
221 format!(
222 r#"
223namespace slint::private_api {{
224#define SLINT_DECL_ITEM(ItemName) \
225 extern const cbindgen_private::ItemVTable ItemName##VTable; \
226 extern SLINT_DLL_IMPORT const cbindgen_private::ItemVTable* slint_get_##ItemName##VTable();
227
228extern "C" {{
229{}
230}}
231
232#undef SLINT_DECL_ITEM
233}}
234"#,
235 items
236 .iter()
237 .map(|item_name| format!("SLINT_DECL_ITEM({});", item_name))
238 .collect::<Vec<_>>()
239 .join("\n")
240 )
241}
242
243fn gen_corelib(
244 root_dir: &Path,
245 include_dir: &Path,
246 dependencies: &mut Vec<PathBuf>,
247 enabled_features: EnabledFeatures,
248) -> anyhow::Result<()> {
249 let mut config = default_config();
250
251 let items = [
252 "Empty",
253 "Rectangle",
254 "BasicBorderRectangle",
255 "BorderRectangle",
256 "ImageItem",
257 "ClippedImage",
258 "TouchArea",
259 "FocusScope",
260 "Flickable",
261 "Text",
262 "Path",
263 "WindowItem",
264 "TextInput",
265 "Clip",
266 "BoxShadow",
267 "Rotate",
268 "Opacity",
269 "Layer",
270 ];
271
272 config.export.include = [
273 "Clipboard",
274 "ItemTreeVTable",
275 "Slice",
276 "WindowAdapterRcOpaque",
277 "PropertyAnimation",
278 "EasingCurve",
279 "TextHorizontalAlignment",
280 "TextVerticalAlignment",
281 "TextOverflow",
282 "TextWrap",
283 "ImageFit",
284 "FillRule",
285 "MouseCursor",
286 "InputType",
287 "StandardButtonKind",
288 "DialogButtonRole",
289 "PointerEventKind",
290 "PointerEventButton",
291 "PointerEvent",
292 "PointerScrollEvent",
293 "Rect",
294 "SortOrder",
295 "BitmapFont",
296 ]
297 .iter()
298 .chain(items.iter())
299 .map(|x| x.to_string())
300 .collect();
301
302 let mut private_exported_types: std::collections::HashSet<String> =
303 config.export.include.iter().cloned().collect();
304
305 // included in generated_public.h
306 let public_exported_types = [
307 "RenderingState",
308 "SetRenderingNotifierError",
309 "GraphicsAPI",
310 "CloseRequestResponse",
311 "StandardListViewItem",
312 "Rgb8Pixel",
313 "Rgba8Pixel",
314 ];
315
316 config.export.exclude = [
317 "SharedString",
318 "SharedVector",
319 "ImageInner",
320 "ImageCacheKey",
321 "Image",
322 "Color",
323 "PathData",
324 "PathElement",
325 "Brush",
326 "slint_new_path_elements",
327 "slint_new_path_events",
328 "Property",
329 "Slice",
330 "Timer",
331 "TimerMode",
332 "PropertyHandleOpaque",
333 "Callback",
334 "slint_property_listener_scope_evaluate",
335 "slint_property_listener_scope_is_dirty",
336 "PropertyTrackerOpaque",
337 "CallbackOpaque",
338 "WindowAdapterRc",
339 "VoidArg",
340 "KeyEventArg",
341 "PointerEventArg",
342 "PointerScrollEventArg",
343 "PointArg",
344 "Point",
345 "slint_color_brighter",
346 "slint_color_darker",
347 "slint_color_transparentize",
348 "slint_color_mix",
349 "slint_color_with_alpha",
350 "slint_image_size",
351 "slint_image_path",
352 "slint_image_load_from_path",
353 "slint_image_load_from_embedded_data",
354 "slint_image_from_embedded_textures",
355 "slint_image_compare_equal",
356 "slint_image_set_nine_slice_edges",
357 "slint_timer_start",
358 "slint_timer_singleshot",
359 "slint_timer_destroy",
360 "slint_timer_stop",
361 "slint_timer_restart",
362 "slint_timer_running",
363 "Coord",
364 "LogicalRect",
365 "LogicalPoint",
366 "LogicalLength",
367 ]
368 .iter()
369 .chain(public_exported_types.iter())
370 .map(|x| x.to_string())
371 .collect();
372
373 let mut crate_dir = root_dir.to_owned();
374 crate_dir.extend(["internal", "core"].iter());
375
376 ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
377
378 let mut string_config = config.clone();
379 string_config.export.exclude = vec!["SharedString".into()];
380 string_config.export.body.insert(
381 "Slice".to_owned(),
382 " const T &operator[](int i) const { return ptr[i]; }
383 /// Note: this doesn't initialize Slice properly, but we need to keep the struct as compatible with C
384 constexpr Slice() = default;
385 /// Rust uses a NonNull, so even empty slices shouldn't use nullptr
386 constexpr Slice(const T *ptr, uintptr_t len) : ptr(ptr ? const_cast<T*>(ptr) : reinterpret_cast<T*>(sizeof(T))), len(len) {}
387 "
388 .to_owned(),
389 );
390 cbindgen::Builder::new()
391 .with_config(string_config)
392 .with_src(crate_dir.join("string.rs"))
393 .with_src(crate_dir.join("slice.rs"))
394 .with_after_include("namespace slint { struct SharedString; }")
395 .generate()
396 .context("Unable to generate bindings for slint_string_internal.h")?
397 .write_to_file(include_dir.join("slint_string_internal.h"));
398
399 cbindgen::Builder::new()
400 .with_config(config.clone())
401 .with_src(crate_dir.join("sharedvector.rs"))
402 .with_after_include("namespace slint { template<typename T> struct SharedVector; }")
403 .generate()
404 .context("Unable to generate bindings for slint_sharedvector_internal.h")?
405 .write_to_file(include_dir.join("slint_sharedvector_internal.h"));
406
407 let mut properties_config = config.clone();
408 properties_config.export.exclude.clear();
409 properties_config.structure.derive_eq = true;
410 properties_config.structure.derive_neq = true;
411 private_exported_types.extend(properties_config.export.include.iter().cloned());
412 cbindgen::Builder::new()
413 .with_config(properties_config)
414 .with_src(crate_dir.join("properties.rs"))
415 .with_src(crate_dir.join("properties/ffi.rs"))
416 .with_src(crate_dir.join("callbacks.rs"))
417 .with_after_include("namespace slint { class Color; class Brush; }")
418 .generate()
419 .context("Unable to generate bindings for slint_properties_internal.h")?
420 .write_to_file(include_dir.join("slint_properties_internal.h"));
421
422 // slint_timer_internal.h:
423 let timer_config = {
424 let mut tmp = config.clone();
425 tmp.export.include = vec![
426 "TimerMode",
427 "slint_timer_start",
428 "slint_timer_singleshot",
429 "slint_timer_destroy",
430 "slint_timer_stop",
431 "slint_timer_restart",
432 "slint_timer_running",
433 ]
434 .iter()
435 .map(|s| s.to_string())
436 .collect();
437 tmp.export.exclude = config
438 .export
439 .exclude
440 .iter()
441 .filter(|exclusion| !tmp.export.include.iter().any(|inclusion| inclusion == *exclusion))
442 .cloned()
443 .collect();
444 tmp
445 };
446 cbindgen::Builder::new()
447 .with_config(timer_config)
448 .with_src(crate_dir.join("timers.rs"))
449 .generate()
450 .context("Unable to generate bindings for slint_timer_internal.h")?
451 .write_to_file(include_dir.join("slint_timer_internal.h"));
452
453 for (rust_types, extra_excluded_types, internal_header, prelude) in [
454 (
455 vec![
456 "ImageInner",
457 "Image",
458 "ImageCacheKey",
459 "Size",
460 "slint_image_size",
461 "slint_image_path",
462 "slint_image_load_from_path",
463 "slint_image_load_from_embedded_data",
464 "slint_image_from_embedded_textures",
465 "slint_image_compare_equal",
466 "slint_image_set_nine_slice_edges",
467 "SharedPixelBuffer",
468 "SharedImageBuffer",
469 "StaticTextures",
470 "BorrowedOpenGLTextureOrigin"
471 ],
472 vec!["Color"],
473 "slint_image_internal.h",
474 "namespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; namespace types{ struct NineSliceImage{}; } }",
475 ),
476 (
477 vec!["Color", "slint_color_brighter", "slint_color_darker",
478 "slint_color_transparentize",
479 "slint_color_mix",
480 "slint_color_with_alpha",],
481 vec![],
482 "slint_color_internal.h",
483 "",
484 ),
485 (
486 vec!["PathData", "PathElement", "slint_new_path_elements", "slint_new_path_events"],
487 vec![],
488 "slint_pathdata_internal.h",
489 "",
490 ),
491 (
492 vec!["Brush", "LinearGradient", "GradientStop", "RadialGradient"],
493 vec!["Color"],
494 "slint_brush_internal.h",
495 "",
496 ),
497 ]
498 .iter()
499 {
500 let mut special_config = config.clone();
501 special_config.export.include = rust_types.iter().map(|s| s.to_string()).collect();
502 special_config.export.exclude = [
503 "slint_visit_item_tree",
504 "slint_windowrc_drop",
505 "slint_windowrc_clone",
506 "slint_windowrc_show",
507 "slint_windowrc_hide",
508 "slint_windowrc_is_visible",
509 "slint_windowrc_get_scale_factor",
510 "slint_windowrc_set_scale_factor",
511 "slint_windowrc_get_text_input_focused",
512 "slint_windowrc_set_text_input_focused",
513 "slint_windowrc_set_focus_item",
514 "slint_windowrc_set_component",
515 "slint_windowrc_show_popup",
516 "slint_windowrc_close_popup",
517 "slint_windowrc_set_rendering_notifier",
518 "slint_windowrc_request_redraw",
519 "slint_windowrc_on_close_requested",
520 "slint_windowrc_position",
521 "slint_windowrc_set_logical_position",
522 "slint_windowrc_set_physical_position",
523 "slint_windowrc_size",
524 "slint_windowrc_set_logical_size",
525 "slint_windowrc_set_physical_size",
526 "slint_windowrc_dark_color_scheme",
527 "slint_windowrc_dispatch_pointer_event",
528 "slint_windowrc_dispatch_key_event",
529 "slint_windowrc_dispatch_event",
530 "slint_windowrc_set_fullscreen",
531 "slint_windowrc_set_minimized",
532 "slint_windowrc_set_maximized",
533 "slint_windowrc_is_fullscreen",
534 "slint_windowrc_is_minimized",
535 "slint_windowrc_is_maximized",
536 "slint_new_path_elements",
537 "slint_new_path_events",
538 "slint_color_brighter",
539 "slint_color_darker",
540 "slint_color_transparentize",
541 "slint_color_mix",
542 "slint_color_with_alpha",
543 "slint_image_size",
544 "slint_image_path",
545 "slint_image_load_from_path",
546 "slint_image_load_from_embedded_data",
547 "slint_image_set_nine_slice_edges",
548 "slint_image_from_embedded_textures",
549 "slint_image_compare_equal",
550 ]
551 .iter()
552 .filter(|exclusion| !rust_types.iter().any(|inclusion| inclusion == *exclusion))
553 .chain(extra_excluded_types.iter())
554 .chain(public_exported_types.iter())
555 .map(|s| s.to_string())
556 .collect();
557
558 special_config.enumeration = cbindgen::EnumConfig {
559 derive_tagged_enum_copy_assignment: true,
560 derive_tagged_enum_copy_constructor: true,
561 derive_tagged_enum_destructor: true,
562 derive_helper_methods: true,
563 private_default_tagged_enum_constructor: true,
564 ..Default::default()
565 };
566 special_config.structure.derive_eq = true;
567 special_config.structure.derive_neq = true;
568 // Put the rust type in a deeper "types" namespace, so the use of same type in for example generated
569 // Property<> fields uses the public `slint::Blah` type
570 special_config.namespaces =
571 Some(vec!["slint".into(), "cbindgen_private".into(), "types".into()]);
572
573 private_exported_types.extend(special_config.export.include.iter().cloned());
574
575 special_config.after_includes = (!prelude.is_empty()).then(|| prelude.to_string());
576
577 cbindgen::Builder::new()
578 .with_config(special_config)
579 .with_src(crate_dir.join("graphics.rs"))
580 .with_src(crate_dir.join("graphics/color.rs"))
581 .with_src(crate_dir.join("graphics/path.rs"))
582 .with_src(crate_dir.join("graphics/brush.rs"))
583 .with_src(crate_dir.join("graphics/image.rs"))
584 .with_src(crate_dir.join("graphics/image/cache.rs"))
585 .with_src(crate_dir.join("animations.rs"))
586 // .with_src(crate_dir.join("input.rs"))
587 .with_src(crate_dir.join("item_rendering.rs"))
588 .with_src(crate_dir.join("window.rs"))
589 .with_include("slint_enums_internal.h")
590 .generate()
591 .with_context(|| format!("Unable to generate bindings for {}", internal_header))?
592 .write_to_file(include_dir.join(internal_header));
593 }
594
595 // Generate a header file with some public API (enums, etc.)
596 let mut public_config = config.clone();
597 public_config.namespaces = Some(vec!["slint".into()]);
598 public_config.export.item_types = vec![cbindgen::ItemType::Enums, cbindgen::ItemType::Structs];
599 // Previously included types are now excluded (to avoid duplicates)
600 public_config.export.exclude = private_exported_types.into_iter().collect();
601 public_config.export.exclude.push("Point".into());
602 public_config.export.include = public_exported_types.into_iter().map(str::to_string).collect();
603 public_config.export.body.insert(
604 "Rgb8Pixel".to_owned(),
605 "/// \\private\nfriend bool operator==(const Rgb8Pixel&, const Rgb8Pixel&) = default;"
606 .into(),
607 );
608 public_config.export.body.insert(
609 "Rgba8Pixel".to_owned(),
610 "/// \\private\nfriend bool operator==(const Rgba8Pixel&, const Rgba8Pixel&) = default;"
611 .into(),
612 );
613
614 cbindgen::Builder::new()
615 .with_config(public_config)
616 .with_src(crate_dir.join("graphics.rs"))
617 .with_src(crate_dir.join("window.rs"))
618 .with_src(crate_dir.join("api.rs"))
619 .with_src(crate_dir.join("model.rs"))
620 .with_src(crate_dir.join("graphics/image.rs"))
621 .with_include("slint_string.h")
622 .with_after_include(format!(
623 r#"
624/// This macro expands to the to the numeric value of the major version of Slint you're
625/// developing against. For example if you're using version 1.5.2, this macro will expand to 1.
626#define SLINT_VERSION_MAJOR {x}
627/// This macro expands to the to the numeric value of the minor version of Slint you're
628/// developing against. For example if you're using version 1.5.2, this macro will expand to 5.
629#define SLINT_VERSION_MINOR {y}
630/// This macro expands to the to the numeric value of the patch version of Slint you're
631/// developing against. For example if you're using version 1.5.2, this macro will expand to 2.
632#define SLINT_VERSION_PATCH {z}
633/// This macro expands to the string representation of the version of Slint you're developing against.
634/// For example if you're using version 1.5.2, this macro will expand to "1.5.2".
635#define SLINT_VERSION_STRING "{x}.{y}.{z}"
636
637{features}
638"#,
639 x = env!("CARGO_PKG_VERSION_MAJOR"),
640 y = env!("CARGO_PKG_VERSION_MINOR"),
641 z = env!("CARGO_PKG_VERSION_PATCH"),
642 features = enabled_features.defines()
643 ))
644 .generate()
645 .context("Unable to generate bindings for slint_generated_public.h")?
646 .write_to_file(include_dir.join("slint_generated_public.h"));
647
648 config.export.body.insert(
649 "ItemTreeNode".to_owned(),
650 " constexpr ItemTreeNode(Item_Body x) : item {x} {}
651 constexpr ItemTreeNode(DynamicTree_Body x) : dynamic_tree{x} {}"
652 .to_owned(),
653 );
654 config.export.body.insert(
655 "CachedRenderingData".to_owned(),
656 " constexpr CachedRenderingData() : cache_index{}, cache_generation{} {}".to_owned(),
657 );
658 config.export.body.insert(
659 "EasingCurve".to_owned(),
660 " constexpr EasingCurve(EasingCurve::Tag tag = Tag::Linear, float a = 0, float b = 0, float c = 1, float d = 1) : tag(tag), cubic_bezier{{a,b,c,d}} {}".into()
661 );
662 config.export.body.insert(
663 "LayoutInfo".to_owned(),
664 " inline LayoutInfo merge(const LayoutInfo &other) const;
665 friend inline LayoutInfo operator+(const LayoutInfo &a, const LayoutInfo &b) { return a.merge(b); }
666 friend bool operator==(const LayoutInfo&, const LayoutInfo&) = default;".into(),
667 );
668 config.export.body.insert(
669 "WindowEvent".to_owned(),
670 "/* Some members of the WindowEvent enum have destructors (with SharedString), but thankfully we don't use these so we can have an empty constructor */
671 ~WindowEvent() {}"
672 .into(),
673 );
674 config
675 .export
676 .body
677 .insert("Flickable".to_owned(), " inline Flickable(); inline ~Flickable();".into());
678 config.export.pre_body.insert("FlickableDataBox".to_owned(), "struct FlickableData;".into());
679
680 cbindgen::Builder::new()
681 .with_config(config)
682 .with_src(crate_dir.join("lib.rs"))
683 .with_include("slint_config.h")
684 .with_include("vtable.h")
685 .with_include("slint_string.h")
686 .with_include("slint_sharedvector.h")
687 .with_include("slint_properties.h")
688 .with_include("slint_callbacks.h")
689 .with_include("slint_color.h")
690 .with_include("slint_image.h")
691 .with_include("slint_pathdata.h")
692 .with_include("slint_brush.h")
693 .with_include("slint_generated_public.h")
694 .with_include("slint_enums_internal.h")
695 .with_include("slint_point.h")
696 .with_include("slint_timer.h")
697 .with_include("slint_builtin_structs_internal.h")
698 .with_after_include(
699 r"
700namespace slint {
701 namespace private_api { class WindowAdapterRc; }
702 namespace cbindgen_private {
703 using slint::private_api::WindowAdapterRc;
704 using namespace vtable;
705 struct KeyEvent; struct PointerEvent;
706 using private_api::Property;
707 using private_api::PathData;
708 using private_api::Point;
709 struct Rect;
710 using LogicalRect = Rect;
711 using LogicalPoint = Point2D<float>;
712 using LogicalLength = float;
713 struct ItemTreeVTable;
714 struct ItemVTable;
715 using types::IntRect;
716 }
717}",
718 )
719 .with_trailer(gen_item_declarations(&items))
720 .generate()
721 .expect("Unable to generate bindings")
722 .write_to_file(include_dir.join("slint_internal.h"));
723
724 Ok(())
725}
726
727fn gen_backend_qt(
728 root_dir: &Path,
729 include_dir: &Path,
730 dependencies: &mut Vec<PathBuf>,
731) -> anyhow::Result<()> {
732 let mut config = default_config();
733
734 let items = [
735 "NativeButton",
736 "NativeSpinBox",
737 "NativeCheckBox",
738 "NativeSlider",
739 "NativeProgressIndicator",
740 "NativeGroupBox",
741 "NativeLineEdit",
742 "NativeScrollView",
743 "NativeStandardListViewItem",
744 "NativeTableHeaderSection",
745 "NativeComboBox",
746 "NativeComboBoxPopup",
747 "NativeTabWidget",
748 "NativeTab",
749 "NativeStyleMetrics",
750 "NativePalette",
751 ];
752
753 config.export.include = items.iter().map(|x| x.to_string()).collect();
754 config.export.exclude = vec!["FloatArg".into()];
755
756 config.export.body.insert(
757 "NativeStyleMetrics".to_owned(),
758 " inline explicit NativeStyleMetrics(void* = nullptr); inline ~NativeStyleMetrics();"
759 .to_owned(),
760 );
761
762 config.export.body.insert(
763 "NativePalette".to_owned(),
764 " inline explicit NativePalette(void* = nullptr); inline ~NativePalette();".to_owned(),
765 );
766
767 let mut crate_dir = root_dir.to_owned();
768 crate_dir.extend(["internal", "backends", "qt"].iter());
769
770 ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
771
772 cbindgen::Builder::new()
773 .with_config(config)
774 .with_crate(crate_dir)
775 .with_include("slint_internal.h")
776 .with_after_include(
777 r"
778 namespace slint::cbindgen_private {
779 // HACK ALERT: This struct declaration is duplicated in internal/backend/qt/qt_widgets.rs - keep in sync.
780 struct SlintTypeErasedWidget
781 {
782 virtual ~SlintTypeErasedWidget() = 0;
783 SlintTypeErasedWidget(const SlintTypeErasedWidget&) = delete;
784 SlintTypeErasedWidget& operator=(const SlintTypeErasedWidget&) = delete;
785
786 virtual void *qwidget() const = 0;
787 };
788 using SlintTypeErasedWidgetPtr = std::unique_ptr<SlintTypeErasedWidget>;
789 }
790 ",
791 )
792 .with_trailer(gen_item_declarations(&items))
793 .generate()
794 .context("Unable to generate bindings for slint_qt_internal.h")?
795 .write_to_file(include_dir.join("slint_qt_internal.h"));
796
797 Ok(())
798}
799
800fn gen_platform(
801 root_dir: &Path,
802 include_dir: &Path,
803 dependencies: &mut Vec<PathBuf>,
804) -> anyhow::Result<()> {
805 let config: Config = default_config();
806 let mut crate_dir: PathBuf = root_dir.to_owned();
807 crate_dir.extend(["api", "cpp"].iter());
808
809 ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
810
811 cbindgen::Builder::new()
812 .with_config(config)
813 .with_crate(crate_dir)
814 .with_include("slint_image_internal.h")
815 .with_include("slint_internal.h")
816 .with_after_include(
817 r"
818namespace slint::cbindgen_private { struct WindowProperties; }
819",
820 )
821 .generate()
822 .context("Unable to generate bindings for slint_platform_internal.h")?
823 .write_to_file(path:include_dir.join(path:"slint_platform_internal.h"));
824
825 Ok(())
826}
827
828fn gen_interpreter(
829 root_dir: &Path,
830 include_dir: &Path,
831 dependencies: &mut Vec<PathBuf>,
832) -> anyhow::Result<()> {
833 let mut config = default_config();
834 config.export.exclude = IntoIterator::into_iter([
835 "Value",
836 "ValueType",
837 "PropertyDescriptor",
838 "Diagnostic",
839 "PropertyDescriptor",
840 "Box",
841 ])
842 .map(String::from)
843 .collect();
844 let mut crate_dir = root_dir.to_owned();
845
846 crate_dir.extend(["internal", "interpreter"].iter());
847 ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
848
849 // Generate a header file with some public API (enums, etc.)
850 let mut public_config = config.clone();
851 public_config.namespaces = Some(vec!["slint".into(), "interpreter".into()]);
852 public_config.export.item_types = vec![cbindgen::ItemType::Enums, cbindgen::ItemType::Structs];
853
854 public_config.export.exclude = IntoIterator::into_iter([
855 "ComponentCompilerOpaque",
856 "ComponentDefinitionOpaque",
857 "ModelAdaptorVTable",
858 "StructIteratorOpaque",
859 "ComponentInstance",
860 "StructIteratorResult",
861 "Value",
862 "StructOpaque",
863 "ModelNotifyOpaque",
864 ])
865 .map(String::from)
866 .collect();
867
868 cbindgen::Builder::new()
869 .with_config(public_config)
870 .with_crate(crate_dir.clone())
871 .generate()
872 .context("Unable to generate bindings for slint_interpreter_generated_public.h")?
873 .write_to_file(include_dir.join("slint_interpreter_generated_public.h"));
874
875 cbindgen::Builder::new()
876 .with_config(config)
877 .with_crate(crate_dir)
878 .with_include("slint_internal.h")
879 .with_include("slint_interpreter_generated_public.h")
880 .with_after_include(
881 r"
882 namespace slint::cbindgen_private {
883 struct Value;
884 using slint::interpreter::ValueType;
885 using slint::interpreter::PropertyDescriptor;
886 using slint::interpreter::Diagnostic;
887 template <typename T> using Box = T*;
888 }",
889 )
890 .generate()
891 .context("Unable to generate bindings for slint_interpreter_internal.h")?
892 .write_to_file(include_dir.join("slint_interpreter_internal.h"));
893
894 Ok(())
895}
896
897macro_rules! declare_features {
898 ($($f:ident)+) => {
899 #[derive(Clone, Copy)]
900 pub struct EnabledFeatures {
901 $(pub $f: bool,)*
902 }
903 impl EnabledFeatures {
904 /// Generate the `#define`
905 pub fn defines(self) -> String {
906 let mut defines = String::new();
907 $(
908 if self.$f {
909 defines = format!("{defines}#define SLINT_FEATURE_{}\n", stringify!($f).to_ascii_uppercase());
910 };
911 )*
912 defines
913 }
914
915 /// Get the feature from the environment variable set by cargo when building running the slint-cpp's build script
916 #[allow(unused)]
917 pub fn from_env() -> Self {
918 Self {
919 $(
920 $f: std::env::var(format!("CARGO_FEATURE_{}", stringify!($f).to_ascii_uppercase())).is_ok(),
921 )*
922 }
923 }
924 }
925 };
926}
927
928declare_features! {interpreter backend_qt freestanding renderer_software renderer_skia experimental}
929
930/// Generate the headers.
931/// `root_dir` is the root directory of the slint git repo
932/// `include_dir` is the output directory
933/// Returns the list of all paths that contain dependencies to the generated output. If you call this
934/// function from build.rs, feed each entry to stdout prefixed with `cargo:rerun-if-changed=`.
935pub fn gen_all(
936 root_dir: &Path,
937 include_dir: &Path,
938 enabled_features: EnabledFeatures,
939) -> anyhow::Result<Vec<PathBuf>> {
940 proc_macro2::fallback::force(); // avoid a abort if panic=abort is set
941 std::fs::create_dir_all(path:include_dir).context("Could not create the include directory")?;
942 let mut deps: Vec = Vec::new();
943 enums(path:include_dir)?;
944 builtin_structs(path:include_dir)?;
945 gen_corelib(root_dir, include_dir, &mut deps, enabled_features)?;
946 gen_backend_qt(root_dir, include_dir, &mut deps)?;
947 gen_platform(root_dir, include_dir, &mut deps)?;
948 if enabled_features.interpreter {
949 gen_interpreter(root_dir, include_dir, &mut deps)?;
950 }
951 Ok(deps)
952}
953