Add libxkbcommon as a submodule

This commit is contained in:
Jakub Hlusička 2026-01-11 22:37:17 +01:00
parent e69a871c2a
commit a07d13f4e8
11 changed files with 117 additions and 50 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "firmware2/libxkbcommon"]
path = firmware2/libxkbcommon
url = https://github.com/xkbcommon/libxkbcommon

View file

@ -12,6 +12,7 @@ rustflags = [
[env] [env]
LIBXKBCOMMON_BUILD_DIR = "libxkbcommon/build"
ESP_LOG = "warn" ESP_LOG = "warn"
# This is overkill, but we can afford it. # This is overkill, but we can afford it.
SLINT_FONT_SIZES = "8,11,10,12,13,14,15,16,18,20,22,24,32" SLINT_FONT_SIZES = "8,11,10,12,13,14,15,16,18,20,22,24,32"

1
firmware2/Cargo.lock generated
View file

@ -31,6 +31,7 @@ dependencies = [
"esp-storage", "esp-storage",
"gix", "gix",
"i-slint-common", "i-slint-common",
"indoc",
"itertools 0.14.0", "itertools 0.14.0",
"json", "json",
"lazy_static", "lazy_static",

View file

@ -85,6 +85,7 @@ embuild = "0.33"
cc = "1.2.9" cc = "1.2.9"
slint-build = "1.14.1" slint-build = "1.14.1"
gix = { version = "0.76.0", default-features = false, features = ["max-performance", "status"] } gix = { version = "0.76.0", default-features = false, features = ["max-performance", "status"] }
indoc = "2.0.7"
[[bin]] [[bin]]
name = "acid-firmware" name = "acid-firmware"

View file

@ -4,28 +4,46 @@
### Compiling libxkbcommon ### Compiling libxkbcommon
Before compiling the firmware itself, its C dependencies must be compiled separately first.
This needs to be done on Linux or in WSL. This needs to be done on Linux or in WSL.
meson.build needs to be changed such that `pkgconfig.generate` is called with the `variables` field embedded directly, for some reason: Download and install [`eim`, the ESP-IDF Installation Manager](github.com/espressif/idf-im-ui/releases/latest).
Enable the ESP-IDF using:
```meson
variables: [
f'xkb_root=@XKB_CONFIG_ROOT@',
f'xkb_extra_path=@XKB_CONFIG_EXTRA_PATH@',
],
```
Then, the tests, fuzzes and benches need to be commented out.
Afterwards, the following command should succeed in building the static library in debug mode:
```sh ```sh
meson setup build-debug -Denable-x11=false -Dxkb-config-root=/usr/share/X11/xkb -Dx-locale-root=/usr/share/X11/locale -Denable-wayland=false --cross-file cross-esp32s3.txt -Denable-xkbregistry=false -Denable-docs=false -Denable-tools=false --wipe --buildtype=debug eim select
meson compile -C build-debug
``` ```
Afterwards, a static library with redefined externally-linked symbols needs to be generated: Compile the libxkbcommon library.
```sh
./libxkbcommon-compile.sh build
``` ```
path/to/root/libxkbcommon-redefine-syms.sh build-debug/libxkbcommon{,_redefined_syms}.a
### Compiling the firmware
To compile the firmware, run:
```ps1
cargo build --release
```
If you wish to download the firmware on the keyboard, the following command can be used:
```ps1
cargo run --release -- --port=[PORT]
```
where `[PORT]` shall be replaced by a serial port such as `COM5`.
## Building for development
The following arguments are useful to build the libxkbcommon library for debugging.
```sh
./libxkbcommon-compile.sh build-debug --buildtype=debug -Dc_args="-fdebug-prefix-map=@GLOBAL_SOURCE_ROOT@=[PATH_TO_LIBRARY_ON_WINDOWS]"
```
where `[PATH_TO_LIBRARY_ON_WINDOWS]` is the path to the libxkbcommon submodule on Windows.
This replaces the debugging symbols with paths that will be available when debugging on Windows.
Then compile the firmware with:
```ps1
$env:LIBXKBCOMMON_BUILD_DIR="libxkbcommon/build-debug"; cargo build
``` ```
### Creating keymaps ### Creating keymaps

View file

@ -1,20 +1,17 @@
use std::env; use std::env;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::Path; use std::path::{Path, PathBuf};
use const_gen::*; use const_gen::*;
use indoc::formatdoc;
use json::JsonValue; use json::JsonValue;
use slint_build::{CompilerConfiguration, EmbedResourcesKind}; use slint_build::{CompilerConfiguration, EmbedResourcesKind};
use xz2::read::XzEncoder; use xz2::read::XzEncoder;
fn main() { fn main() {
if let Ok(repo) = gix::discover( let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
env::var_os("CARGO_MANIFEST_DIR") if let Ok(repo) = gix::discover(&manifest_dir) {
.unwrap()
.into_string()
.unwrap(),
) {
let commit_hash = repo.head_commit().unwrap().short_id().unwrap(); let commit_hash = repo.head_commit().unwrap().short_id().unwrap();
println!("cargo:rustc-env=GIT_COMMIT_HASH={}", commit_hash); println!("cargo:rustc-env=GIT_COMMIT_HASH={}", commit_hash);
println!( println!(
@ -36,22 +33,27 @@ fn main() {
// println!("cargo:rustc-link-arg=-Tdefmt.x"); // println!("cargo:rustc-link-arg=-Tdefmt.x");
// TODO: Make it a submodule and use relative paths. // TODO: Make it a submodule and use relative paths.
//
// 1. Tell cargo where to find the library
// let lib_path = PathBuf::from(manifest_dir).join("libs");
// println!(r#"cargo:rustc-link-search=native={}"#, lib_path.display());
println!(
r#"cargo:rustc-link-search=native=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug-redefined-syms"#
);
// 2. Link the static library (strip the 'lib' prefix and '.a' extension) // Link libxkbcommon.
let libxkbcommon_build_dir = env::var("LIBXKBCOMMON_BUILD_DIR")
.expect("The build directory of libxkbcommon must be specified using the `LIBXKBCOMMON_BUILD_DIR` environment variable.");
let libxkbcommon_library_path = PathBuf::from(&libxkbcommon_build_dir).join("libxkbcommon.a");
if !libxkbcommon_library_path.is_file() {
panic!(
"{}",
formatdoc! {"
The compiled libxkbcommon library was not found at path {libxkbcommon_library_path:?}.
Most likely, the library was not compiled. See the `README.md` for compilation instructions.
If the `LIBXKBCOMMON_BUILD_DIR` environment variable is not set to the build directory you want to use, change it.
Currently, `LIBXKBCOMMON_BUILD_DIR` is set to: {libxkbcommon_build_dir}
"}
);
}
println!("cargo:rustc-link-search=native={libxkbcommon_build_dir}");
println!("cargo:rustc-link-lib=static=xkbcommon"); println!("cargo:rustc-link-lib=static=xkbcommon");
println!("cargo:rerun-if-changed={libxkbcommon_build_dir}/libxkbcommon.a");
// 3. Re-run if build.rs or the library changes
// println!(r#"cargo:rerun-if-changed=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug\libxkbcommon.a"#);
println!(
r#"cargo:rerun-if-changed=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug-redefined-syms\libxkbcommon.a"#
);
// Slint config and compilation // Slint config and compilation
{ {
@ -62,7 +64,8 @@ fn main() {
// .with_scale_factor(2.0) // .with_scale_factor(2.0)
.with_style("cosmic-dark".to_string()) .with_style("cosmic-dark".to_string())
.embed_resources(EmbedResourcesKind::EmbedForSoftwareRenderer); .embed_resources(EmbedResourcesKind::EmbedForSoftwareRenderer);
slint_build::compile_with_config("ui/main.slint", slint_config).expect("Slint build failed"); slint_build::compile_with_config("ui/main.slint", slint_config)
.expect("Slint build failed");
slint_build::print_rustc_flags().unwrap() slint_build::print_rustc_flags().unwrap()
} }
} }

View file

@ -4,25 +4,24 @@ cpp = 'xtensa-esp32s3-elf-g++'
ar = 'xtensa-esp32s3-elf-ar' ar = 'xtensa-esp32s3-elf-ar'
strip = 'xtensa-esp32s3-elf-strip' strip = 'xtensa-esp32s3-elf-strip'
[build_machine] # It seems like this is not necessary:
system = 'linux' #[build_machine]
cpu_family = 'x86_64' #system = 'linux'
cpu = 'x86_64' #cpu_family = 'x86_64'
endian = 'little' #cpu = 'x86_64'
#endian = 'little'
[host_machine] [host_machine]
system = 'bare-metal' # Crucial: prevents Meson from assuming a POSIX environment system = 'none'
cpu_family = 'xtensa' cpu_family = 'xtensa'
cpu = 'esp32s3' cpu = 'esp32s3'
endian = 'little' endian = 'little'
[built-in options] [built-in options]
c_args = [ c_args = [
'-fdebug-prefix-map=@GLOBAL_SOURCE_ROOT@=C:/Users/Limeth/workspace/c/libxkbcommon-linux',
'-ffreestanding', '-ffreestanding',
'-fno-builtin', '-fno-builtin',
'-mlongcalls', '-mlongcalls',
'-mfix-esp32-psram-cache-issue',
# Manually define the missing inttypes.h macros # Manually define the missing inttypes.h macros
'-DPRId64="lld"', '-DPRId64="lld"',
'-DPRIu64="llu"', '-DPRIu64="llu"',
@ -35,5 +34,4 @@ c_link_args = [
'-nostdlib', '-nostdlib',
'-static' '-static'
] ]
# Ensure we only build static libraries for bare-metal
default_library = 'static' default_library = 'static'

@ -0,0 +1 @@
Subproject commit db1f61ca9a3a3bb05f28550227ca3419e8a22c90

View file

@ -0,0 +1,32 @@
#!/usr/bin/env bash
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <build-directory-name> [args-passed-to-meson-setup]..."
exit 1
fi
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
LIBXKBCOMMON_DIR="$SCRIPT_DIR/libxkbcommon"
BUILD_DIR_NAME="$LIBXKBCOMMON_DIR/$1"
STATIC_LIB_PATH="$BUILD_DIR_NAME/libxkbcommon.a"
SETUP_ARGS=${@:2}
git submodule update --init --recursive
pushd "$LIBXKBCOMMON_DIR" >/dev/null
meson setup "$BUILD_DIR_NAME" \
--wipe \
--cross-file "$SCRIPT_DIR/cross-esp32s3.txt" \
-Denable-x11=false \
-Denable-wayland=false \
-Denable-xkbregistry=false \
-Denable-docs=false \
-Denable-tools=false \
-Dxkb-config-root=/usr/share/X11/xkb \
-Dx-locale-root=/usr/share/X11/locale \
$SETUP_ARGS
meson compile -C "$BUILD_DIR_NAME"
$SCRIPT_DIR/libxkbcommon-redefine-syms.sh "$STATIC_LIB_PATH" "$STATIC_LIB_PATH"
popd >/dev/null
GREEN='\033[0;32m'
RESET='\033[0m'
echo -e "${GREEN}Successfully compiled libxkbcommon. Path to static library:\n$STATIC_LIB_PATH${RESET}"

View file

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
INPUT_LIB=$1 INPUT_LIB=$1
OUTPUT_LIB=$2 OUTPUT_LIB=$2
PREFIX="__xkbc_" PREFIX="__xkbc_"
@ -11,10 +11,14 @@ fi
echo "Redefining symbols from $INPUT_LIB into $OUTPUT_LIB..." echo "Redefining symbols from $INPUT_LIB into $OUTPUT_LIB..."
xtensa-esp-elf-objcopy --redefine-syms=<( xtensa-esp-elf-objcopy --redefine-syms=<(
# Perform set subtraction of undefined symbols and defined symbols, to get globally undefined symbols.
comm -23 \ comm -23 \
<(xtensa-esp-elf-nm -u "$INPUT_LIB" | awk '{print $NF}' | sort -u) \ <(xtensa-esp-elf-nm --undefined-only "$INPUT_LIB" | awk '{print $NF}' | sort -u) \
<(xtensa-esp-elf-nm --defined-only "$INPUT_LIB" | awk '{print $NF}' | sort -u) | \ <(xtensa-esp-elf-nm --defined-only "$INPUT_LIB" | awk '{print $NF}' | sort -u) | \
# Do not prefix already prefixed symbols (idempotency)
grep -v "^${PREFIX}" | \
# Print each symbol followed by the symbol with the prefix
awk -v p="$PREFIX" '{print $1 " " p $1}' awk -v p="$PREFIX" '{print $1 " " p $1}'
) "$INPUT_LIB" "$OUTPUT_LIB" ) "$INPUT_LIB" "$OUTPUT_LIB"
echo "Done! Prefixed all externally linked symbols with $PREFIX." echo "Prefixed all externally linked symbols in $INPUT_LIB with $PREFIX, and written the result to: $OUTPUT_LIB"

View file

@ -56,6 +56,11 @@ pub unsafe extern "C" fn __xkbc_fread(
todo!() todo!()
} }
#[unsafe(no_mangle)]
unsafe extern "C" fn __xkbc_fputs(s: *const c_char, stream: *mut FILE) -> c_int {
todo!()
}
// Printing // Printing
#[unsafe(no_mangle)] #[unsafe(no_mangle)]