Files
RedBear-OS/local/recipes/system/redbear-keymapd/source/src/xkb.rs
T
vasilito c68ace12de feat: relibc S1 — sem_open refcounting + glibc cross-reference assessment
Phase S1 (Critical Correctness):
- sem_open/sem_close: global refcounting via BTreeMap + AtomicUsize
- sem_close: decrements refcount, munmaps only at zero
- sem_open: reuses existing mapping, O_EXCL returns EEXIST
- sem_unlink: marks entry for removal before shm_unlink
- va_list parsing: reads mode_t and value from stack after oflag
- All 11 sem_* functions verified in libc.so T

Phase S2-S4 (Designed, documented):
- eventfd() function, signalfd read path, EINTR handling
- name canonicalization, cancellation safety
- Full plan in local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md

Reference: glibc 2.41 cloned to local/reference/glibc/

Boot verified: greeter ready on VT 3 with refcounted semaphores
2026-05-05 21:12:08 +01:00

344 lines
9.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::collections::HashMap;
use crate::keymap::{Keymap, KeymapEntry};
fn xkb_keycode_to_scancode(code: &str) -> Option<u8> {
match code {
"TLDE" => Some(0x29),
"AE01" => Some(0x02),
"AE02" => Some(0x03),
"AE03" => Some(0x04),
"AE04" => Some(0x05),
"AE05" => Some(0x06),
"AE06" => Some(0x07),
"AE07" => Some(0x08),
"AE08" => Some(0x09),
"AE09" => Some(0x0A),
"AE10" => Some(0x0B),
"AE11" => Some(0x0C),
"AE12" => Some(0x0D),
"AD01" => Some(0x10),
"AD02" => Some(0x11),
"AD03" => Some(0x12),
"AD04" => Some(0x13),
"AD05" => Some(0x14),
"AD06" => Some(0x15),
"AD07" => Some(0x16),
"AD08" => Some(0x17),
"AD09" => Some(0x18),
"AD10" => Some(0x19),
"AD11" => Some(0x1A),
"AD12" => Some(0x1B),
"AC01" => Some(0x1E),
"AC02" => Some(0x1F),
"AC03" => Some(0x20),
"AC04" => Some(0x21),
"AC05" => Some(0x22),
"AC06" => Some(0x23),
"AC07" => Some(0x24),
"AC08" => Some(0x25),
"AC09" => Some(0x26),
"AC10" => Some(0x27),
"AC11" => Some(0x28),
"BKSL" => Some(0x2B),
"AB01" => Some(0x2C),
"AB02" => Some(0x2D),
"AB03" => Some(0x2E),
"AB04" => Some(0x2F),
"AB05" => Some(0x30),
"AB06" => Some(0x31),
"AB07" => Some(0x32),
"AB08" => Some(0x33),
"AB09" => Some(0x34),
"AB10" => Some(0x35),
"SPCE" => Some(0x39),
"LSGT" => Some(0x56),
"BKSP" => Some(0x0E),
"TAB" => Some(0x0F),
"RTRN" => Some(0x1C),
_ => None,
}
}
fn keysym_to_char(sym: &str) -> char {
if sym.len() == 1 {
return sym.chars().next().unwrap_or('\0');
}
match sym {
"space" => ' ',
"exclam" => '!',
"quotedbl" => '"',
"numbersign" => '#',
"dollar" => '$',
"percent" => '%',
"ampersand" => '&',
"apostrophe" => '\'',
"quoteright" => '\'',
"parenleft" => '(',
"parenright" => ')',
"asterisk" => '*',
"plus" => '+',
"comma" => ',',
"minus" => '-',
"period" => '.',
"slash" => '/',
"colon" => ':',
"semicolon" => ';',
"less" => '<',
"equal" => '=',
"greater" => '>',
"question" => '?',
"at" => '@',
"bracketleft" => '[',
"backslash" => '\\',
"bracketright" => ']',
"asciicircum" => '^',
"underscore" => '_',
"grave" => '`',
"braceleft" => '{',
"bar" => '|',
"braceright" => '}',
"asciitilde" => '~',
"nobreakspace" => '\u{00A0}',
"exclamdown" => '¡',
"cent" => '¢',
"sterling" => '£',
"currency" => '¤',
"yen" => '¥',
"brokenbar" => '¦',
"section" => '§',
"diaeresis" => '¨',
"copyright" => '©',
"ordfeminine" => 'ª',
"guillemotleft" => '«',
"notsign" => '¬',
"hyphen" => '\u{00AD}',
"registered" => '®',
"macron" => '¯',
"degree" => '°',
"plusminus" => '±',
"twosuperior" => '²',
"threesuperior" => '³',
"acute" => '´',
"mu" => 'µ',
"paragraph" => '¶',
"periodcentered" => '·',
"cedilla" => '¸',
"onesuperior" => '¹',
"masculine" => 'º',
"guillemotright" => '»',
"onequarter" => '¼',
"onehalf" => '½',
"threequarters" => '¾',
"questiondown" => '¿',
"Agrave" => 'À',
"Aacute" => 'Á',
"Acircumflex" => 'Â',
"Atilde" => 'Ã',
"Adiaeresis" => 'Ä',
"Aring" => 'Å',
"AE" => 'Æ',
"Ccedilla" => 'Ç',
"Egrave" => 'È',
"Eacute" => 'É',
"Ecircumflex" => 'Ê',
"Ediaeresis" => 'Ë',
"Igrave" => 'Ì',
"Iacute" => 'Í',
"Icircumflex" => 'Î',
"Idiaeresis" => 'Ï',
"ETH" => 'Ð',
"Ntilde" => 'Ñ',
"Ograve" => 'Ò',
"Oacute" => 'Ó',
"Ocircumflex" => 'Ô',
"Otilde" => 'Õ',
"Odiaeresis" => 'Ö',
"multiply" => '×',
"Ooblique" => 'Ø',
"Ugrave" => 'Ù',
"Uacute" => 'Ú',
"Ucircumflex" => 'Û',
"Udiaeresis" => 'Ü',
"Yacute" => 'Ý',
"THORN" => 'Þ',
"ssharp" => 'ß',
"agrave" => 'à',
"aacute" => 'á',
"acircumflex" => 'â',
"atilde" => 'ã',
"adiaeresis" => 'ä',
"aring" => 'å',
"ae" => 'æ',
"ccedilla" => 'ç',
"egrave" => 'è',
"eacute" => 'é',
"ecircumflex" => 'ê',
"ediaeresis" => 'ë',
"igrave" => 'ì',
"iacute" => 'í',
"icircumflex" => 'î',
"idiaeresis" => 'ï',
"eth" => 'ð',
"ntilde" => 'ñ',
"ograve" => 'ò',
"oacute" => 'ó',
"ocircumflex" => 'ô',
"otilde" => 'õ',
"odiaeresis" => 'ö',
"division" => '÷',
"oslash" => 'ø',
"ugrave" => 'ù',
"uacute" => 'ú',
"ucircumflex" => 'û',
"udiaeresis" => 'ü',
"yacute" => 'ý',
"thorn" => 'þ',
"ydiaeresis" => 'ÿ',
"EuroSign" => '€',
"NoSymbol" => '\0',
_ => '\0',
}
}
struct XkbKeyEntry {
scancode: u8,
normal: char,
shifted: char,
altgr: char,
altgr_shifted: char,
}
fn parse_keysyms(syms: &[&str]) -> (char, char, char, char) {
let normal = syms
.get(0)
.map(|s| keysym_to_char(s.trim()))
.unwrap_or('\0');
let shifted = syms
.get(1)
.map(|s| keysym_to_char(s.trim()))
.unwrap_or('\0');
let altgr = syms
.get(2)
.map(|s| keysym_to_char(s.trim()))
.unwrap_or('\0');
let altgr_shifted = syms
.get(3)
.map(|s| keysym_to_char(s.trim()))
.unwrap_or('\0');
(normal, shifted, altgr, altgr_shifted)
}
pub fn parse_xkb_symbols(content: &str, variant: Option<&str>) -> Result<Keymap, String> {
let target_variant = variant.unwrap_or("basic");
let mut entries: HashMap<u8, KeymapEntry> = HashMap::new();
let mut found_variant = false;
let mut i = 0;
let lines: Vec<&str> = content.lines().collect();
while i < lines.len() {
let line = lines[i].trim();
if line.starts_with("xkb_symbols") {
let name = extract_variant_name(line).unwrap_or("basic");
if name != target_variant {
i = skip_brace_block(&lines, i + 1);
continue;
}
found_variant = true;
i += 1;
while i < lines.len() {
let inner = lines[i].trim();
if inner.starts_with('}') {
break;
}
if inner.starts_with("key <") {
if let Some(entry) = parse_key_line(inner) {
entries
.entry(entry.scancode)
.or_insert_with(|| KeymapEntry {
scancode: entry.scancode,
normal: entry.normal,
shifted: entry.shifted,
altgr: entry.altgr,
altgr_shifted: entry.altgr_shifted,
});
}
}
i += 1;
}
}
i += 1;
}
if !found_variant {
return Err(format!(
"variant '{}' not found in XKB symbols file",
target_variant
));
}
Ok(Keymap {
name: variant.unwrap_or("basic").to_string(),
entries,
compose: Vec::new(),
dead_keys: Vec::new(),
})
}
fn extract_variant_name(line: &str) -> Option<&str> {
let start = line.find('"')?;
let rest = &line[start + 1..];
let end = rest.find('"')?;
Some(&rest[..end])
}
fn skip_brace_block(lines: &[&str], start: usize) -> usize {
let mut depth = 1;
let mut i = start;
while i < lines.len() && depth > 0 {
for ch in lines[i].chars() {
match ch {
'{' => depth += 1,
'}' => depth -= 1,
_ => {}
}
}
i += 1;
}
i
}
fn parse_key_line(line: &str) -> Option<XkbKeyEntry> {
let key_start = line.find('<')?;
let key_end = line[key_start + 1..].find('>')?;
let keycode = &line[key_start + 1..key_start + 1 + key_end];
let scancode = xkb_keycode_to_scancode(keycode)?;
let syms_start = line.find('[')?;
let syms_end = line.rfind(']')?;
let syms_content = &line[syms_start + 1..syms_end];
let syms: Vec<&str> = syms_content.split(',').collect();
let (normal, shifted, altgr, altgr_shifted) = parse_keysyms(&syms);
Some(XkbKeyEntry {
scancode,
normal,
shifted,
altgr,
altgr_shifted,
})
}
pub fn load_xkb_keymap(
xkb_dir: &str,
layout: &str,
variant: Option<&str>,
) -> Result<Keymap, String> {
let file_path = format!("{}/symbols/{}", xkb_dir, layout);
let content = std::fs::read_to_string(&file_path)
.map_err(|e| format!("failed to read XKB symbols file '{}': {}", file_path, e))?;
parse_xkb_symbols(&content, variant)
}