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 | |
4 | use anyhow::Context; |
5 | use std::io::{BufWriter, Write}; |
6 | use std::path::{Path, PathBuf}; |
7 | |
8 | // cSpell: ignore compat constexpr corelib deps sharedvector pathdata |
9 | |
10 | fn 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 | /// ``` |
69 | namespace 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 | |
86 | fn 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 | |
168 | fn 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 | |
182 | fn 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 | |
220 | fn gen_item_declarations(items: &[&str]) -> String { |
221 | format!( |
222 | r#" |
223 | namespace 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 | |
228 | extern "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 | |
243 | fn 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" |
700 | namespace 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 | |
727 | fn 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 | |
800 | fn 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" |
818 | namespace 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 | |
828 | fn 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 | |
897 | macro_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 | |
928 | declare_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=`. |
935 | pub 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 | |