Skip to content

Commit

Permalink
Merge pull request #921 from psychon/xkbcommon-example
Browse files Browse the repository at this point in the history
Add xkbcommon-example
  • Loading branch information
mergify[bot] authored Mar 3, 2024
2 parents 397930b + d030ec3 commit 08ce9fa
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ jobs:
if: matrix.rust == 'nightly'
run: cargo install grcov

- name: Install xkbcommon
run: sudo apt-get install libxkbcommon-x11-dev

# build
- name: cargo build with all features
run: cargo build --workspace --verbose --all-targets --all-features
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ members = [
"x11rb-async",
"cairo-example",
"xtrace-example",
"xkbcommon-example",
]
15 changes: 15 additions & 0 deletions xkbcommon-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "xkbcommon-example"
version = "0.0.0"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies.x11rb]
path = "../x11rb"
features = ["allow-unsafe-code", "xkb"]

[dependencies.xkbcommon]
version = "0.7"
features = ["x11"]
175 changes: 175 additions & 0 deletions xkbcommon-example/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use x11rb::atom_manager;
use x11rb::connection::{Connection as _, RequestConnection as _};
use x11rb::errors::ReplyOrIdError;
use x11rb::protocol::xkb::{self, ConnectionExt as _};
use x11rb::protocol::xproto::{
self, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, WindowClass,
};
use x11rb::protocol::Event;
use x11rb::wrapper::ConnectionExt as _;
use x11rb::xcb_ffi::XCBConnection;
use xkbcommon::xkb as xkbc;

// A collection of the atoms we will need.
atom_manager! {
pub AtomCollection: AtomCollectionCookie {
WM_PROTOCOLS,
WM_DELETE_WINDOW,
_NET_WM_NAME,
UTF8_STRING,
}
}

/// Handle a single key press or key release event by printing some details
fn handle_key(event: xproto::KeyPressEvent, press: bool, state: &xkbc::State) {
let kind = if press { " Pressed" } else { "Released" };
let sym = state.key_get_one_sym(event.detail.into());
let utf8 = state.key_get_utf8(event.detail.into());
println!("{} keysym {sym:?} for utf8 '{utf8}'", kind);

// Just as an example on how this works:
if sym == xkbc::keysyms::KEY_BackSpace.into() {
println!("Pressed key was backspace");
}
}

/// Create and return a window
fn create_window(
conn: &XCBConnection,
screen_num: usize,
atoms: &AtomCollection,
) -> Result<xproto::Window, ReplyOrIdError> {
let screen = &conn.setup().roots[screen_num];
let window = conn.generate_id()?;
conn.create_window(
screen.root_depth,
window,
screen.root,
0,
0,
100,
100,
0,
WindowClass::INPUT_OUTPUT,
screen.root_visual,
&CreateWindowAux::new()
.background_pixel(screen.white_pixel)
.event_mask(EventMask::KEY_PRESS | EventMask::KEY_RELEASE),
)?;
let title = "Keyboard tester";
conn.change_property8(
PropMode::REPLACE,
window,
xproto::AtomEnum::WM_NAME,
xproto::AtomEnum::STRING,
title.as_bytes(),
)?;
conn.change_property8(
PropMode::REPLACE,
window,
atoms._NET_WM_NAME,
atoms.UTF8_STRING,
title.as_bytes(),
)?;
conn.change_property32(
PropMode::REPLACE,
window,
atoms.WM_PROTOCOLS,
xproto::AtomEnum::ATOM,
&[atoms.WM_DELETE_WINDOW],
)?;
conn.change_property8(
PropMode::REPLACE,
window,
xproto::AtomEnum::WM_CLASS,
xproto::AtomEnum::STRING,
b"simple_window\0simple_window\0",
)?;
Ok(window)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let (conn, screen_num) = XCBConnection::connect(None)?;

conn.prefetch_extension_information(xkb::X11_EXTENSION_NAME)?;
let atoms = AtomCollection::new(&conn)?;
let xkb = conn.xkb_use_extension(1, 0)?;
let atoms = atoms.reply()?;
let xkb = xkb.reply()?;
assert!(
xkb.supported,
"This program requires the X11 server to support the XKB extension"
);

// Ask the X11 server to send us XKB events.
// TODO: No idea what to pick here. I guess this is asking unnecessarily for too much?
let events = xkb::EventType::NEW_KEYBOARD_NOTIFY
| xkb::EventType::MAP_NOTIFY
| xkb::EventType::STATE_NOTIFY;
// TODO: No idea what to pick here. I guess this is asking unnecessarily for too much?
let map_parts = xkb::MapPart::KEY_TYPES
| xkb::MapPart::KEY_SYMS
| xkb::MapPart::MODIFIER_MAP
| xkb::MapPart::EXPLICIT_COMPONENTS
| xkb::MapPart::KEY_ACTIONS
| xkb::MapPart::KEY_BEHAVIORS
| xkb::MapPart::VIRTUAL_MODS
| xkb::MapPart::VIRTUAL_MOD_MAP;
conn.xkb_select_events(
xkb::ID::USE_CORE_KBD.into(),
0u8.into(),
events,
map_parts,
map_parts,
&xkb::SelectEventsAux::new(),
)?;

// Set up xkbcommon state and get the current keymap.
let context = xkbc::Context::new(xkbc::CONTEXT_NO_FLAGS);
let device_id = xkbc::x11::get_core_keyboard_device_id(&conn);
assert!(device_id >= 0);
let keymap = xkbc::x11::keymap_new_from_device(
&context,
&conn,
device_id,
xkbc::KEYMAP_COMPILE_NO_FLAGS,
);
let mut state = xkbc::x11::state_new_from_device(&keymap, &conn, device_id);

// Create and show a window
let window = create_window(&conn, screen_num, &atoms)?;
conn.map_window(window)?;
conn.flush()?;

// Main loop; wait for events
loop {
match conn.wait_for_event()? {
Event::ClientMessage(event) => {
let data = event.data.as_data32();
if event.format == 32 && event.window == window && data[0] == atoms.WM_DELETE_WINDOW
{
println!("Window was asked to close");
break;
}
}
Event::XkbStateNotify(event) => {
if i32::from(event.device_id) == device_id {
// Inform xkbcommon that the keyboard state changed
state.update_mask(
event.base_mods.into(),
event.latched_mods.into(),
event.locked_mods.into(),
event.base_group.try_into().unwrap(),
event.latched_group.try_into().unwrap(),
event.locked_group.into(),
);
}
}
Event::KeyPress(event) => handle_key(event, true, &state),
Event::KeyRelease(event) => handle_key(event, false, &state),
event => println!("Ignoring event {event:?}"),
}
}

Ok(())
}

0 comments on commit 08ce9fa

Please sign in to comment.