From a07d13f4e8e3ceb6b37eb344d8c8562763ba63c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Hlusi=C4=8Dka?= Date: Sun, 11 Jan 2026 22:37:17 +0100 Subject: [PATCH] Add libxkbcommon as a submodule --- .gitmodules | 3 ++ firmware2/.cargo/config.toml | 1 + firmware2/Cargo.lock | 1 + firmware2/Cargo.toml | 1 + firmware2/README.md | 48 +++++++++++++++++-------- firmware2/build.rs | 47 ++++++++++++------------ firmware2/cross-esp32s3.txt | 16 ++++----- firmware2/libxkbcommon | 1 + firmware2/libxkbcommon-compile.sh | 32 +++++++++++++++++ firmware2/libxkbcommon-redefine-syms.sh | 12 ++++--- firmware2/src/ffi/inout/mod.rs | 5 +++ 11 files changed, 117 insertions(+), 50 deletions(-) create mode 100644 .gitmodules create mode 160000 firmware2/libxkbcommon create mode 100644 firmware2/libxkbcommon-compile.sh diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b6e5733 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "firmware2/libxkbcommon"] + path = firmware2/libxkbcommon + url = https://github.com/xkbcommon/libxkbcommon diff --git a/firmware2/.cargo/config.toml b/firmware2/.cargo/config.toml index fb541c7..08e05bf 100644 --- a/firmware2/.cargo/config.toml +++ b/firmware2/.cargo/config.toml @@ -12,6 +12,7 @@ rustflags = [ [env] +LIBXKBCOMMON_BUILD_DIR = "libxkbcommon/build" ESP_LOG = "warn" # This is overkill, but we can afford it. SLINT_FONT_SIZES = "8,11,10,12,13,14,15,16,18,20,22,24,32" diff --git a/firmware2/Cargo.lock b/firmware2/Cargo.lock index c366752..f6882ef 100644 --- a/firmware2/Cargo.lock +++ b/firmware2/Cargo.lock @@ -31,6 +31,7 @@ dependencies = [ "esp-storage", "gix", "i-slint-common", + "indoc", "itertools 0.14.0", "json", "lazy_static", diff --git a/firmware2/Cargo.toml b/firmware2/Cargo.toml index b46c2d4..7a67c50 100644 --- a/firmware2/Cargo.toml +++ b/firmware2/Cargo.toml @@ -85,6 +85,7 @@ embuild = "0.33" cc = "1.2.9" slint-build = "1.14.1" gix = { version = "0.76.0", default-features = false, features = ["max-performance", "status"] } +indoc = "2.0.7" [[bin]] name = "acid-firmware" diff --git a/firmware2/README.md b/firmware2/README.md index df5a17b..fec9b16 100644 --- a/firmware2/README.md +++ b/firmware2/README.md @@ -4,28 +4,46 @@ ### 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. -meson.build needs to be changed such that `pkgconfig.generate` is called with the `variables` field embedded directly, for some reason: - -```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: +Download and install [`eim`, the ESP-IDF Installation Manager](github.com/espressif/idf-im-ui/releases/latest). +Enable the ESP-IDF using: ```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 -meson compile -C build-debug +eim select ``` -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 diff --git a/firmware2/build.rs b/firmware2/build.rs index c86c925..4940899 100644 --- a/firmware2/build.rs +++ b/firmware2/build.rs @@ -1,20 +1,17 @@ use std::env; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; -use std::path::Path; +use std::path::{Path, PathBuf}; use const_gen::*; +use indoc::formatdoc; use json::JsonValue; use slint_build::{CompilerConfiguration, EmbedResourcesKind}; use xz2::read::XzEncoder; fn main() { - if let Ok(repo) = gix::discover( - env::var_os("CARGO_MANIFEST_DIR") - .unwrap() - .into_string() - .unwrap(), - ) { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + if let Ok(repo) = gix::discover(&manifest_dir) { let commit_hash = repo.head_commit().unwrap().short_id().unwrap(); println!("cargo:rustc-env=GIT_COMMIT_HASH={}", commit_hash); println!( @@ -36,22 +33,27 @@ fn main() { // println!("cargo:rustc-link-arg=-Tdefmt.x"); // 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"); - - // 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"# - ); + println!("cargo:rerun-if-changed={libxkbcommon_build_dir}/libxkbcommon.a"); // Slint config and compilation { @@ -62,7 +64,8 @@ fn main() { // .with_scale_factor(2.0) .with_style("cosmic-dark".to_string()) .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() } } diff --git a/firmware2/cross-esp32s3.txt b/firmware2/cross-esp32s3.txt index edeae7d..437a019 100644 --- a/firmware2/cross-esp32s3.txt +++ b/firmware2/cross-esp32s3.txt @@ -4,25 +4,24 @@ cpp = 'xtensa-esp32s3-elf-g++' ar = 'xtensa-esp32s3-elf-ar' strip = 'xtensa-esp32s3-elf-strip' -[build_machine] -system = 'linux' -cpu_family = 'x86_64' -cpu = 'x86_64' -endian = 'little' +# It seems like this is not necessary: +#[build_machine] +#system = 'linux' +#cpu_family = 'x86_64' +#cpu = 'x86_64' +#endian = 'little' [host_machine] -system = 'bare-metal' # Crucial: prevents Meson from assuming a POSIX environment +system = 'none' cpu_family = 'xtensa' cpu = 'esp32s3' endian = 'little' [built-in options] c_args = [ - '-fdebug-prefix-map=@GLOBAL_SOURCE_ROOT@=C:/Users/Limeth/workspace/c/libxkbcommon-linux', '-ffreestanding', '-fno-builtin', '-mlongcalls', - '-mfix-esp32-psram-cache-issue', # Manually define the missing inttypes.h macros '-DPRId64="lld"', '-DPRIu64="llu"', @@ -35,5 +34,4 @@ c_link_args = [ '-nostdlib', '-static' ] -# Ensure we only build static libraries for bare-metal default_library = 'static' diff --git a/firmware2/libxkbcommon b/firmware2/libxkbcommon new file mode 160000 index 0000000..db1f61c --- /dev/null +++ b/firmware2/libxkbcommon @@ -0,0 +1 @@ +Subproject commit db1f61ca9a3a3bb05f28550227ca3419e8a22c90 diff --git a/firmware2/libxkbcommon-compile.sh b/firmware2/libxkbcommon-compile.sh new file mode 100644 index 0000000..2637d55 --- /dev/null +++ b/firmware2/libxkbcommon-compile.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +if [ "$#" -lt 1 ]; then + echo "Usage: $0 [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}" diff --git a/firmware2/libxkbcommon-redefine-syms.sh b/firmware2/libxkbcommon-redefine-syms.sh index 1c9925c..fd81f24 100644 --- a/firmware2/libxkbcommon-redefine-syms.sh +++ b/firmware2/libxkbcommon-redefine-syms.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash INPUT_LIB=$1 OUTPUT_LIB=$2 PREFIX="__xkbc_" @@ -11,10 +11,14 @@ fi echo "Redefining symbols from $INPUT_LIB into $OUTPUT_LIB..." xtensa-esp-elf-objcopy --redefine-syms=<( + # Perform set subtraction of undefined symbols and defined symbols, to get globally undefined symbols. 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) | \ - awk -v p="$PREFIX" '{print $1 " " p $1}' + # 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}' ) "$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" diff --git a/firmware2/src/ffi/inout/mod.rs b/firmware2/src/ffi/inout/mod.rs index 70168a6..6c39c39 100644 --- a/firmware2/src/ffi/inout/mod.rs +++ b/firmware2/src/ffi/inout/mod.rs @@ -56,6 +56,11 @@ pub unsafe extern "C" fn __xkbc_fread( todo!() } +#[unsafe(no_mangle)] +unsafe extern "C" fn __xkbc_fputs(s: *const c_char, stream: *mut FILE) -> c_int { + todo!() +} + // Printing #[unsafe(no_mangle)]