use std::{env, path::PathBuf}; use bindgen::callbacks::{EnumVariantValue, ParseCallbacks}; use convert_case::{Case, Casing}; #[derive(Debug)] struct RemovePrefixesCallback {} impl RemovePrefixesCallback { fn new() -> Self { Self {} } fn boxed() -> Box { Box::new(Self::new()) } } impl ParseCallbacks for RemovePrefixesCallback { fn item_name(&self, original_item_name: &str) -> Option { const PREFIXES: &[&str] = &["XKB_", "xkb_"]; let mut ret = None; for prefix in PREFIXES { if let Some(name) = original_item_name.strip_prefix(prefix) { ret = Some(name.to_owned()); break; } } ret } fn enum_variant_name( &self, enum_name: Option<&str>, original_variant_name: &str, _variant_value: EnumVariantValue, ) -> Option { if enum_name.is_none() { return self.item_name(original_variant_name); } // let original_variant_name = self // .item_name(original_variant_name) // .unwrap_or(original_variant_name.to_owned()); let enum_name = enum_name.unwrap(); let enum_name = enum_name.strip_prefix("enum ").unwrap_or(enum_name); let enum_lower = original_variant_name.to_lowercase(); let diff_index = enum_lower .chars() .zip(enum_name.chars()) .enumerate() .find(|(_i, (v, e))| v != e) .map(|it| it.0) .unwrap_or(enum_name.len()); let name = original_variant_name[diff_index..] .trim_start_matches('_') .to_case(Case::Pascal); Some(name) } } #[derive(Debug)] struct TypedefCallback {} impl TypedefCallback { fn new() -> Self { Self {} } fn boxed() -> Box { Box::new(Self::new()) } } impl ParseCallbacks for TypedefCallback { fn item_name(&self, original_item_name: &str) -> Option { if original_item_name == "xkb_keysym_t" { Some(String::from("Keysym")) } else { None } } // fn int_macro(&self, name: &str, _value: i64) -> Option { // if name.starts_with("XKB_KEY_") { // Some(IntKind::Custom { // name: "Keysym", // is_signed: false, // }) // } else { // None // } // } // fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec { // if (info.name.contains("keysym") && !info.name.contains("flags")) // || info.name == "Keysym" // || info.name == "xkb_keysym_t" // || info.name == "keysym_t" // { // panic!("{info:?}"); // vec![ // "PartialEq".to_string(), // "PartialOrd".to_string(), // "Ord".to_string(), // "Eq".to_string(), // ] // } else { // vec![] // } // } } // #[derive(Debug)] // struct VariadicsCallback {} // // impl VariadicsCallback { // fn new() -> Self { // Self {} // } // fn boxed() -> Box { // Box::new(Self::new()) // } // } // // impl ParseCallbacks for VariadicsCallback { // fn wrap_as_variadic_fn(&self, name: &str) -> Option { // panic!("mdr {name}"); // Some(format!("{name}_var")) // } // } fn build_xkb_keysyms() -> anyhow::Result<()> { let common_bindings = bindgen::builder() .header("/usr/include/xkbcommon/xkbcommon-keysyms.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(RemovePrefixesCallback::boxed()) .parse_callbacks(TypedefCallback::boxed()) .allowlist_var("XKB_.*") .generate()?; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let out_file = out_path.join("libxkbcommon-keysyms.rs"); common_bindings .write_to_file(out_file) .expect("Couldn't write bindings!"); Ok(()) } fn build_xkbcommon() -> anyhow::Result<()> { let common_bindings = bindgen::builder() .header("ffi/logger.h") .header("/usr/include/xkbcommon/xkbcommon.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(RemovePrefixesCallback::boxed()) .parse_callbacks(TypedefCallback::boxed()) // .parse_callbacks(VariadicsCallback::boxed()) .allowlist_item("xkb_.*") .allowlist_item("log_.*") // Log wrapper // .allowlist_item("XKB_.*") .new_type_alias_deref("Keysym") .no_debug("xkb_keysym_t") .allowlist_var("XKB_KEYCODE_INVALID") .allowlist_var("XKB_LAYOUT_INVALID") .allowlist_var("XKB_LEVEL_INVALID") .allowlist_var("XKB_MOD_INVALID") .allowlist_var("XKB_LED_INVALID") .allowlist_var("XKB_KEYCODE_MAX") .allowlist_var("XKB_KEYSYM_MAX") .default_enum_style(bindgen::EnumVariation::NewType { is_bitfield: false, is_global: false, }) .derive_eq(true) .derive_partialeq(true) .derive_ord(true) .derive_partialord(true) .derive_hash(true) .generate()?; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let out_file = out_path.join("libxkbcommon.rs"); common_bindings .write_to_file(out_file) .expect("Couldn't write bindings!"); println!("cargo:rustc-link-lib=xkbcommon"); Ok(()) } fn build_logger() -> anyhow::Result<()> { cc::Build::new() .file("ffi/logger.c") .std("c17") .warnings(true) .warnings_into_errors(true) .extra_warnings(true) .compile("logger"); Ok(()) } fn main() -> anyhow::Result<()> { build_xkbcommon()?; build_xkb_keysyms()?; build_logger()?; Ok(()) }