Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
24 changes: 20 additions & 4 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ jobs:
uses: Swatinem/rust-cache@v2

- name: Run criterion benchmarks
run: cargo bench --bench bench_speed
run: |
cargo bench --bench bench_setup
cargo bench --bench bench_key
cargo bench --bench bench_compose

- name: Run memory benchmark
run: |
Expand All @@ -43,10 +46,23 @@ jobs:
strip target/release/examples/bench_size_wkb
strip target/release/examples/bench_size_xkbcommon
strip target/release/examples/bench_size_xkbcommon_dl
# Find libxkbcommon.so and get its stripped size (xkbcommon/xkbcommon-dl depend on it)
LIBXKB=$(find /usr/lib64 /usr/lib -name 'libxkbcommon.so.0.*' 2>/dev/null | head -1)
if [ -n "$LIBXKB" ]; then
strip --strip-all -o /tmp/libxkbcommon_stripped.so "$LIBXKB"
LIBXKB_SIZE=$(stat -c%s /tmp/libxkbcommon_stripped.so)
else
LIBXKB_SIZE=0
fi
WKB_SIZE=$(stat -c%s target/release/examples/bench_size_wkb)
XKB_BIN=$(stat -c%s target/release/examples/bench_size_xkbcommon)
XKB_DL_BIN=$(stat -c%s target/release/examples/bench_size_xkbcommon_dl)
XKB_TOTAL=$((XKB_BIN + LIBXKB_SIZE))
XKB_DL_TOTAL=$((XKB_DL_BIN + LIBXKB_SIZE))
{
echo "wkb $(stat -c%s target/release/examples/bench_size_wkb)"
echo "xkbcommon $(stat -c%s target/release/examples/bench_size_xkbcommon)"
echo "xkbcommon-dl $(stat -c%s target/release/examples/bench_size_xkbcommon_dl)"
echo "wkb ${WKB_SIZE}"
echo "xkbcommon ${XKB_TOTAL}"
echo "xkbcommon-dl ${XKB_DL_TOTAL}"
} > /tmp/size_output.txt

- name: Generate benchmark table
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,14 @@ jobs:
- compose
- ctrl_keys
- keymap
- compile
- type
- layouts
- led_lights
- level
- modifiers
- repeat
- unicode
steps:
- name: Install system dependencies
run: dnf install -y gcc libxkbcommon-devel xkeyboard-config libX11-common
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
**/target
**/test_files
!/test_files
**/Cargo.lock
/.vscode
/.opencode
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,15 @@ xkbcommon-compat = { path = "xkbcommon-compat" }
criterion = { version = "0.5", features = ["html_reports"] }

[[bench]]
name = "bench_speed"
name = "bench_setup"
harness = false

[[bench]]
name = "bench_key"
harness = false

[[bench]]
name = "bench_compose"
harness = false

[[example]]
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# wkb — Wayland Keyboard

[![Crates.io](https://img.shields.io/crates/v/wayland-keyboard.svg)](https://crates.io/crates/wayland-keyboard)
[![Documentation](https://docs.rs/wayland-keyboard/badge.svg)](https://docs.rs/wayland-keyboard)
[![License](https://img.shields.io/crates/l/wayland-keyboard.svg)](https://github.com/rano-oss/wkb/blob/main/LICENSE)
[![Test Status](https://img.shields.io/github/actions/workflow/status/rano-oss/wkb/tests.yml?branch=main&event=push&label=tests)](https://github.com/rano-oss/wkb/actions)

A lightweight, pure Rust keyboard handling library for Wayland. WKB is a
drop-in alternative to `xkbcommon` that compiles XKB keymaps, tracks modifier
and compose state, and maps evdev key codes to characters — all without C
Expand All @@ -20,6 +25,11 @@ dependencies.

## Quick Start

```toml
[dependencies]
wayland-keyboard = "0.1"
```

```rust
use wkb::{WKB, KeyDirection};

Expand Down
252 changes: 252 additions & 0 deletions benches/bench_compose.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
mod common;

use common::*;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use std::ffi::CString;
use std::time::Duration;
use wkb::testing::ListComposerTestExt;

fn cfg() -> Criterion {
Criterion::default()
.warm_up_time(Duration::from_millis(50))
.measurement_time(Duration::from_millis(200))
.sample_size(10)
}

fn bench_compose_table_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("compose/table_creation");
let locale = COMPOSE_LOCALE;

group.bench_function("wkb", |b| {
b.iter(|| {
let resolved = xkb_core::compose::resolve_compose_file(black_box(locale));
if let Some(subpath) = resolved {
let path = std::path::Path::new("/usr/share/X11/locale").join(&subpath);
let composer = wkb::testing::compose_parse::load_compose_from_path(&path);
black_box(composer);
}
});
});

group.bench_function("xkbcommon", |b| {
use xkbcommon::xkb;
let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let locale_os = std::ffi::OsStr::new(locale);
b.iter(|| {
let table = xkb::compose::Table::new_from_locale(
&ctx,
locale_os,
xkb::compose::COMPILE_NO_FLAGS,
);
let _ = black_box(table);
});
});

group.bench_function("xkbcommon-dl", |b| {
let xkb = xkbcommon_dl::xkbcommon_handle();
let xkb_compose = xkbcommon_dl::xkbcommon_compose_handle();
let ctx =
unsafe { (xkb.xkb_context_new)(xkbcommon_dl::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
let c_locale = CString::new(locale).unwrap();
b.iter(|| {
let table = unsafe {
(xkb_compose.xkb_compose_table_new_from_locale)(
ctx,
c_locale.as_ptr(),
xkbcommon_dl::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
)
};
black_box(table);
if !table.is_null() {
unsafe { (xkb_compose.xkb_compose_table_unref)(table) };
}
});
unsafe { (xkb.xkb_context_unref)(ctx) };
});

group.finish();
}

fn bench_compose_state_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("compose/state_creation");
let locale = COMPOSE_LOCALE;

{
let resolved = xkb_core::compose::resolve_compose_file(locale);
let path = resolved.map(|s| {
std::path::Path::new("/usr/share/X11/locale")
.join(&s)
.to_path_buf()
});
group.bench_function("wkb", |b| {
b.iter(|| {
if let Some(ref p) = path {
let composer = wkb::testing::compose_parse::load_compose_from_path(p);
black_box(composer);
}
});
});
}

{
use xkbcommon::xkb;
let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let locale_os = std::ffi::OsStr::new(locale);
let table =
xkb::compose::Table::new_from_locale(&ctx, locale_os, xkb::compose::COMPILE_NO_FLAGS)
.expect("compose table");
group.bench_function("xkbcommon", |b| {
b.iter(|| {
let state = xkb::compose::State::new(&table, xkb::compose::STATE_NO_FLAGS);
black_box(state);
});
});
}

{
let xkb = xkbcommon_dl::xkbcommon_handle();
let xkb_compose = xkbcommon_dl::xkbcommon_compose_handle();
let ctx =
unsafe { (xkb.xkb_context_new)(xkbcommon_dl::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
let c_locale = CString::new(locale).unwrap();
let table = unsafe {
(xkb_compose.xkb_compose_table_new_from_locale)(
ctx,
c_locale.as_ptr(),
xkbcommon_dl::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
)
};
group.bench_function("xkbcommon-dl", |b| {
b.iter(|| {
let state = unsafe {
(xkb_compose.xkb_compose_state_new)(
table,
xkbcommon_dl::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
)
};
black_box(state);
if !state.is_null() {
unsafe { (xkb_compose.xkb_compose_state_unref)(state) };
}
});
});
unsafe {
(xkb_compose.xkb_compose_table_unref)(table);
(xkb.xkb_context_unref)(ctx);
};
}

group.finish();
}

fn bench_compose_feed(c: &mut Criterion) {
let mut group = c.benchmark_group("compose/feed");

for seq in COMPOSE_SEQUENCES {
{
let resolved = xkb_core::compose::resolve_compose_file(COMPOSE_LOCALE);
let path = resolved.map(|s| {
std::path::Path::new("/usr/share/X11/locale")
.join(&s)
.to_path_buf()
});
if let Some(ref p) = path {
let mut composer = wkb::testing::compose_parse::load_compose_from_path(p);
let tokens: Vec<wkb::testing::Token> = seq
.keysyms
.iter()
.filter_map(|&ks| {
xkb_core::keysym_utf::keysym_to_char(ks).map(wkb::testing::Token::Char)
})
.collect();
group.bench_with_input(BenchmarkId::new("wkb", seq.name), &tokens, |b, tokens| {
b.iter(|| {
for token in tokens {
black_box(composer.feed(*token));
}
});
});
}
}

{
use xkbcommon::xkb;
let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let locale_os = std::ffi::OsStr::new(COMPOSE_LOCALE);
if let Ok(table) = xkb::compose::Table::new_from_locale(
&ctx,
locale_os,
xkb::compose::COMPILE_NO_FLAGS,
) {
let mut state = xkb::compose::State::new(&table, xkb::compose::STATE_NO_FLAGS);
group.bench_with_input(
BenchmarkId::new("xkbcommon", seq.name),
&seq.keysyms,
|b, keysyms| {
b.iter(|| {
for &ks in *keysyms {
black_box(state.feed(xkb::Keysym::new(ks)));
}
state.reset();
});
},
);
}
}

{
let xkb = xkbcommon_dl::xkbcommon_handle();
let xkb_compose = xkbcommon_dl::xkbcommon_compose_handle();
let ctx = unsafe {
(xkb.xkb_context_new)(xkbcommon_dl::xkb_context_flags::XKB_CONTEXT_NO_FLAGS)
};
let c_locale = CString::new(COMPOSE_LOCALE).unwrap();
let table = unsafe {
(xkb_compose.xkb_compose_table_new_from_locale)(
ctx,
c_locale.as_ptr(),
xkbcommon_dl::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
)
};
if !table.is_null() {
let state = unsafe {
(xkb_compose.xkb_compose_state_new)(
table,
xkbcommon_dl::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
)
};
group.bench_with_input(
BenchmarkId::new("xkbcommon-dl", seq.name),
&seq.keysyms,
|b, keysyms| {
b.iter(|| {
for &ks in *keysyms {
black_box(unsafe {
(xkb_compose.xkb_compose_state_feed)(state, ks)
});
}
unsafe { (xkb_compose.xkb_compose_state_reset)(state) };
});
},
);
unsafe {
(xkb_compose.xkb_compose_state_unref)(state);
(xkb_compose.xkb_compose_table_unref)(table);
(xkb.xkb_context_unref)(ctx);
};
}
}
}

group.finish();
}

criterion_group! {
name = benches;
config = cfg();
targets =
bench_compose_table_creation,
bench_compose_state_creation,
bench_compose_feed,
}
criterion_main!(benches);
Loading