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
This commit is contained in:
2026-05-05 21:12:08 +01:00
parent f31522130f
commit 702ec7efac
14 changed files with 1201 additions and 279 deletions
@@ -79,8 +79,8 @@ impl Keymap {
dead_keys: Vec<DeadKeyEntry>,
}
let file: KeymapFile = serde_json::from_str(json_str)
.map_err(|e| format!("parse error: {}", e))?;
let file: KeymapFile =
serde_json::from_str(json_str).map_err(|e| format!("parse error: {}", e))?;
let mut map = HashMap::new();
for entry in file.entries {
@@ -119,192 +119,609 @@ impl BuiltinKeymaps {
fn common_dead_keys() -> Vec<DeadKeyEntry> {
vec![
DeadKeyEntry { dead_key: '`', base: 'a', composed: 'à' },
DeadKeyEntry { dead_key: '`', base: 'e', composed: 'è' },
DeadKeyEntry { dead_key: '`', base: 'i', composed: 'ì' },
DeadKeyEntry { dead_key: '`', base: 'o', composed: 'ò' },
DeadKeyEntry { dead_key: '`', base: 'u', composed: 'ù' },
DeadKeyEntry { dead_key: '`', base: 'A', composed: 'À' },
DeadKeyEntry { dead_key: '`', base: 'E', composed: 'È' },
DeadKeyEntry { dead_key: '`', base: 'I', composed: 'Ì' },
DeadKeyEntry { dead_key: '`', base: 'O', composed: 'Ò' },
DeadKeyEntry { dead_key: '`', base: 'U', composed: 'Ù' },
DeadKeyEntry { dead_key: '\'', base: 'a', composed: 'á' },
DeadKeyEntry { dead_key: '\'', base: 'e', composed: 'é' },
DeadKeyEntry { dead_key: '\'', base: 'i', composed: 'í' },
DeadKeyEntry { dead_key: '\'', base: 'o', composed: 'ó' },
DeadKeyEntry { dead_key: '\'', base: 'u', composed: 'ú' },
DeadKeyEntry { dead_key: '\'', base: 'A', composed: 'Á' },
DeadKeyEntry { dead_key: '\'', base: 'E', composed: 'É' },
DeadKeyEntry { dead_key: '\'', base: 'I', composed: 'Í' },
DeadKeyEntry { dead_key: '\'', base: 'O', composed: 'Ó' },
DeadKeyEntry { dead_key: '\'', base: 'U', composed: 'Ú' },
DeadKeyEntry { dead_key: '^', base: 'a', composed: 'â' },
DeadKeyEntry { dead_key: '^', base: 'e', composed: 'ê' },
DeadKeyEntry { dead_key: '^', base: 'i', composed: 'î' },
DeadKeyEntry { dead_key: '^', base: 'o', composed: 'ô' },
DeadKeyEntry { dead_key: '^', base: 'u', composed: 'û' },
DeadKeyEntry { dead_key: '^', base: 'A', composed: 'Â' },
DeadKeyEntry { dead_key: '^', base: 'E', composed: 'Ê' },
DeadKeyEntry { dead_key: '^', base: 'I', composed: 'Î' },
DeadKeyEntry { dead_key: '^', base: 'O', composed: 'Ô' },
DeadKeyEntry { dead_key: '^', base: 'U', composed: 'Û' },
DeadKeyEntry { dead_key: '"', base: 'a', composed: 'ä' },
DeadKeyEntry { dead_key: '"', base: 'e', composed: 'ë' },
DeadKeyEntry { dead_key: '"', base: 'i', composed: 'ï' },
DeadKeyEntry { dead_key: '"', base: 'o', composed: 'ö' },
DeadKeyEntry { dead_key: '"', base: 'u', composed: 'ü' },
DeadKeyEntry { dead_key: '"', base: 'A', composed: 'Ä' },
DeadKeyEntry { dead_key: '"', base: 'E', composed: 'Ë' },
DeadKeyEntry { dead_key: '"', base: 'I', composed: 'Ï' },
DeadKeyEntry { dead_key: '"', base: 'O', composed: 'Ö' },
DeadKeyEntry { dead_key: '"', base: 'U', composed: 'Ü' },
DeadKeyEntry { dead_key: '~', base: 'a', composed: 'ã' },
DeadKeyEntry { dead_key: '~', base: 'n', composed: 'ñ' },
DeadKeyEntry { dead_key: '~', base: 'o', composed: 'õ' },
DeadKeyEntry { dead_key: '~', base: 'A', composed: 'Ã' },
DeadKeyEntry { dead_key: '~', base: 'N', composed: 'Ñ' },
DeadKeyEntry { dead_key: '~', base: 'O', composed: 'Õ' },
DeadKeyEntry {
dead_key: '`',
base: 'a',
composed: 'à',
},
DeadKeyEntry {
dead_key: '`',
base: 'e',
composed: 'è',
},
DeadKeyEntry {
dead_key: '`',
base: 'i',
composed: 'ì',
},
DeadKeyEntry {
dead_key: '`',
base: 'o',
composed: 'ò',
},
DeadKeyEntry {
dead_key: '`',
base: 'u',
composed: 'ù',
},
DeadKeyEntry {
dead_key: '`',
base: 'A',
composed: 'À',
},
DeadKeyEntry {
dead_key: '`',
base: 'E',
composed: 'È',
},
DeadKeyEntry {
dead_key: '`',
base: 'I',
composed: 'Ì',
},
DeadKeyEntry {
dead_key: '`',
base: 'O',
composed: 'Ò',
},
DeadKeyEntry {
dead_key: '`',
base: 'U',
composed: 'Ù',
},
DeadKeyEntry {
dead_key: '\'',
base: 'a',
composed: 'á',
},
DeadKeyEntry {
dead_key: '\'',
base: 'e',
composed: 'é',
},
DeadKeyEntry {
dead_key: '\'',
base: 'i',
composed: 'í',
},
DeadKeyEntry {
dead_key: '\'',
base: 'o',
composed: 'ó',
},
DeadKeyEntry {
dead_key: '\'',
base: 'u',
composed: 'ú',
},
DeadKeyEntry {
dead_key: '\'',
base: 'A',
composed: 'Á',
},
DeadKeyEntry {
dead_key: '\'',
base: 'E',
composed: 'É',
},
DeadKeyEntry {
dead_key: '\'',
base: 'I',
composed: 'Í',
},
DeadKeyEntry {
dead_key: '\'',
base: 'O',
composed: 'Ó',
},
DeadKeyEntry {
dead_key: '\'',
base: 'U',
composed: 'Ú',
},
DeadKeyEntry {
dead_key: '^',
base: 'a',
composed: 'â',
},
DeadKeyEntry {
dead_key: '^',
base: 'e',
composed: 'ê',
},
DeadKeyEntry {
dead_key: '^',
base: 'i',
composed: 'î',
},
DeadKeyEntry {
dead_key: '^',
base: 'o',
composed: 'ô',
},
DeadKeyEntry {
dead_key: '^',
base: 'u',
composed: 'û',
},
DeadKeyEntry {
dead_key: '^',
base: 'A',
composed: 'Â',
},
DeadKeyEntry {
dead_key: '^',
base: 'E',
composed: 'Ê',
},
DeadKeyEntry {
dead_key: '^',
base: 'I',
composed: 'Î',
},
DeadKeyEntry {
dead_key: '^',
base: 'O',
composed: 'Ô',
},
DeadKeyEntry {
dead_key: '^',
base: 'U',
composed: 'Û',
},
DeadKeyEntry {
dead_key: '"',
base: 'a',
composed: 'ä',
},
DeadKeyEntry {
dead_key: '"',
base: 'e',
composed: 'ë',
},
DeadKeyEntry {
dead_key: '"',
base: 'i',
composed: 'ï',
},
DeadKeyEntry {
dead_key: '"',
base: 'o',
composed: 'ö',
},
DeadKeyEntry {
dead_key: '"',
base: 'u',
composed: 'ü',
},
DeadKeyEntry {
dead_key: '"',
base: 'A',
composed: 'Ä',
},
DeadKeyEntry {
dead_key: '"',
base: 'E',
composed: 'Ë',
},
DeadKeyEntry {
dead_key: '"',
base: 'I',
composed: 'Ï',
},
DeadKeyEntry {
dead_key: '"',
base: 'O',
composed: 'Ö',
},
DeadKeyEntry {
dead_key: '"',
base: 'U',
composed: 'Ü',
},
DeadKeyEntry {
dead_key: '~',
base: 'a',
composed: 'ã',
},
DeadKeyEntry {
dead_key: '~',
base: 'n',
composed: 'ñ',
},
DeadKeyEntry {
dead_key: '~',
base: 'o',
composed: 'õ',
},
DeadKeyEntry {
dead_key: '~',
base: 'A',
composed: 'Ã',
},
DeadKeyEntry {
dead_key: '~',
base: 'N',
composed: 'Ñ',
},
DeadKeyEntry {
dead_key: '~',
base: 'O',
composed: 'Õ',
},
]
}
fn make_us() -> Keymap {
let mut entries = HashMap::new();
let bindings: &[(u8, char, char)] = &[
(0x01, '\x1B', '\x1B'), (0x02, '1', '!'), (0x03, '2', '@'),
(0x04, '3', '#'), (0x05, '4', '$'), (0x06, '5', '%'),
(0x07, '6', '^'), (0x08, '7', '&'), (0x09, '8', '*'),
(0x0A, '9', '('), (0x0B, '0', ')'), (0x0C, '-', '_'),
(0x0D, '=', '+'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
(0x10, 'q', 'Q'), (0x11, 'w', 'W'), (0x12, 'e', 'E'),
(0x13, 'r', 'R'), (0x14, 't', 'T'), (0x15, 'y', 'Y'),
(0x16, 'u', 'U'), (0x17, 'i', 'I'), (0x18, 'o', 'O'),
(0x19, 'p', 'P'), (0x1A, '[', '{'), (0x1B, ']', '}'),
(0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 's', 'S'),
(0x20, 'd', 'D'), (0x21, 'f', 'F'), (0x22, 'g', 'G'),
(0x23, 'h', 'H'), (0x24, 'j', 'J'), (0x25, 'k', 'K'),
(0x26, 'l', 'L'), (0x27, ';', ':'), (0x28, '\'', '"'),
(0x29, '`', '~'), (0x2B, '\\', '|'), (0x2C, 'z', 'Z'),
(0x2D, 'x', 'X'), (0x2E, 'c', 'C'), (0x2F, 'v', 'V'),
(0x30, 'b', 'B'), (0x31, 'n', 'N'), (0x32, 'm', 'M'),
(0x33, ',', '<'), (0x34, '.', '>'), (0x35, '/', '?'),
(0x01, '\x1B', '\x1B'),
(0x02, '1', '!'),
(0x03, '2', '@'),
(0x04, '3', '#'),
(0x05, '4', '$'),
(0x06, '5', '%'),
(0x07, '6', '^'),
(0x08, '7', '&'),
(0x09, '8', '*'),
(0x0A, '9', '('),
(0x0B, '0', ')'),
(0x0C, '-', '_'),
(0x0D, '=', '+'),
(0x0E, '\x7F', '\x7F'),
(0x0F, '\t', '\t'),
(0x10, 'q', 'Q'),
(0x11, 'w', 'W'),
(0x12, 'e', 'E'),
(0x13, 'r', 'R'),
(0x14, 't', 'T'),
(0x15, 'y', 'Y'),
(0x16, 'u', 'U'),
(0x17, 'i', 'I'),
(0x18, 'o', 'O'),
(0x19, 'p', 'P'),
(0x1A, '[', '{'),
(0x1B, ']', '}'),
(0x1C, '\n', '\n'),
(0x1E, 'a', 'A'),
(0x1F, 's', 'S'),
(0x20, 'd', 'D'),
(0x21, 'f', 'F'),
(0x22, 'g', 'G'),
(0x23, 'h', 'H'),
(0x24, 'j', 'J'),
(0x25, 'k', 'K'),
(0x26, 'l', 'L'),
(0x27, ';', ':'),
(0x28, '\'', '"'),
(0x29, '`', '~'),
(0x2B, '\\', '|'),
(0x2C, 'z', 'Z'),
(0x2D, 'x', 'X'),
(0x2E, 'c', 'C'),
(0x2F, 'v', 'V'),
(0x30, 'b', 'B'),
(0x31, 'n', 'N'),
(0x32, 'm', 'M'),
(0x33, ',', '<'),
(0x34, '.', '>'),
(0x35, '/', '?'),
(0x39, ' ', ' '),
];
for &(sc, normal, shifted) in bindings {
entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' });
entries.insert(
sc,
KeymapEntry {
scancode: sc,
normal,
shifted,
altgr: '\0',
altgr_shifted: '\0',
},
);
}
let dead_keys = Self::common_dead_keys();
Keymap { name: "us".into(), entries, compose: Vec::new(), dead_keys }
Keymap {
name: "us".into(),
entries,
compose: Vec::new(),
dead_keys,
}
}
fn make_gb() -> Keymap {
let mut km = Self::make_us();
km.name = "gb".into();
if let Some(e) = km.entries.get_mut(&0x29) { e.shifted = '¬'; }
if let Some(e) = km.entries.get_mut(&0x2B) { e.normal = '#'; e.shifted = '~'; }
if let Some(e) = km.entries.get_mut(&0x29) {
e.shifted = '¬';
}
if let Some(e) = km.entries.get_mut(&0x2B) {
e.normal = '#';
e.shifted = '~';
}
km
}
fn make_dvorak() -> Keymap {
let mut entries = HashMap::new();
let remap: &[(u8, char, char)] = &[
(0x01, '\x1B', '\x1B'), (0x02, '1', '!'), (0x03, '2', '@'),
(0x04, '3', '#'), (0x05, '4', '$'), (0x06, '5', '%'),
(0x07, '6', '^'), (0x08, '7', '&'), (0x09, '8', '*'),
(0x0A, '9', '('), (0x0B, '0', ')'), (0x0C, '[', '{'),
(0x0D, ']', '}'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
(0x10, '\'', '"'), (0x11, ',', '<'), (0x12, '.', '>'),
(0x13, 'p', 'P'), (0x14, 'y', 'Y'), (0x15, 'f', 'F'),
(0x16, 'g', 'G'), (0x17, 'c', 'C'), (0x18, 'r', 'R'),
(0x19, 'l', 'L'), (0x1A, '/', '?'), (0x1B, '=', '+'),
(0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 'o', 'O'),
(0x20, 'e', 'E'), (0x21, 'u', 'U'), (0x22, 'i', 'I'),
(0x23, 'd', 'D'), (0x24, 'h', 'H'), (0x25, 't', 'T'),
(0x26, 'n', 'N'), (0x27, 's', 'S'), (0x28, '-', '_'),
(0x29, '`', '~'), (0x2B, '\\', '|'), (0x2C, ';', ':'),
(0x2D, 'q', 'Q'), (0x2E, 'j', 'J'), (0x2F, 'k', 'K'),
(0x30, 'x', 'X'), (0x31, 'b', 'B'), (0x32, 'm', 'M'),
(0x33, 'w', 'W'), (0x34, 'v', 'V'), (0x35, 'z', 'Z'),
(0x01, '\x1B', '\x1B'),
(0x02, '1', '!'),
(0x03, '2', '@'),
(0x04, '3', '#'),
(0x05, '4', '$'),
(0x06, '5', '%'),
(0x07, '6', '^'),
(0x08, '7', '&'),
(0x09, '8', '*'),
(0x0A, '9', '('),
(0x0B, '0', ')'),
(0x0C, '[', '{'),
(0x0D, ']', '}'),
(0x0E, '\x7F', '\x7F'),
(0x0F, '\t', '\t'),
(0x10, '\'', '"'),
(0x11, ',', '<'),
(0x12, '.', '>'),
(0x13, 'p', 'P'),
(0x14, 'y', 'Y'),
(0x15, 'f', 'F'),
(0x16, 'g', 'G'),
(0x17, 'c', 'C'),
(0x18, 'r', 'R'),
(0x19, 'l', 'L'),
(0x1A, '/', '?'),
(0x1B, '=', '+'),
(0x1C, '\n', '\n'),
(0x1E, 'a', 'A'),
(0x1F, 'o', 'O'),
(0x20, 'e', 'E'),
(0x21, 'u', 'U'),
(0x22, 'i', 'I'),
(0x23, 'd', 'D'),
(0x24, 'h', 'H'),
(0x25, 't', 'T'),
(0x26, 'n', 'N'),
(0x27, 's', 'S'),
(0x28, '-', '_'),
(0x29, '`', '~'),
(0x2B, '\\', '|'),
(0x2C, ';', ':'),
(0x2D, 'q', 'Q'),
(0x2E, 'j', 'J'),
(0x2F, 'k', 'K'),
(0x30, 'x', 'X'),
(0x31, 'b', 'B'),
(0x32, 'm', 'M'),
(0x33, 'w', 'W'),
(0x34, 'v', 'V'),
(0x35, 'z', 'Z'),
(0x39, ' ', ' '),
];
for &(sc, normal, shifted) in remap {
entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' });
entries.insert(
sc,
KeymapEntry {
scancode: sc,
normal,
shifted,
altgr: '\0',
altgr_shifted: '\0',
},
);
}
Keymap {
name: "dvorak".into(),
entries,
compose: Vec::new(),
dead_keys: Self::common_dead_keys(),
}
Keymap { name: "dvorak".into(), entries, compose: Vec::new(), dead_keys: Self::common_dead_keys() }
}
fn make_azerty() -> Keymap {
let mut entries = HashMap::new();
let bindings: &[(u8, char, char)] = &[
(0x01, '\x1B', '\x1B'), (0x02, '&', '1'), (0x03, 'é', '2'),
(0x04, '"', '3'), (0x05, '\'', '4'), (0x06, '(', '5'),
(0x07, '-', '6'), (0x08, 'è', '7'), (0x09, '_', '8'),
(0x0A, 'ç', '9'), (0x0B, 'à', '0'), (0x0C, ')', '°'),
(0x0D, '=', '+'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
(0x10, 'a', 'A'), (0x11, 'z', 'Z'), (0x12, 'e', 'E'),
(0x13, 'r', 'R'), (0x14, 't', 'T'), (0x15, 'y', 'Y'),
(0x16, 'u', 'U'), (0x17, 'i', 'I'), (0x18, 'o', 'O'),
(0x19, 'p', 'P'), (0x1A, '^', '¨'), (0x1B, '$', '£'),
(0x1C, '\n', '\n'), (0x1E, 'q', 'Q'), (0x1F, 's', 'S'),
(0x20, 'd', 'D'), (0x21, 'f', 'F'), (0x22, 'g', 'G'),
(0x23, 'h', 'H'), (0x24, 'j', 'J'), (0x25, 'k', 'K'),
(0x26, 'l', 'L'), (0x27, 'm', 'M'), (0x28, 'ù', '%'),
(0x29, '²', '~'), (0x2B, '*', 'µ'), (0x2C, 'w', 'W'),
(0x2D, 'x', 'X'), (0x2E, 'c', 'C'), (0x2F, 'v', 'V'),
(0x30, 'b', 'B'), (0x31, 'n', 'N'), (0x32, ',', '?'),
(0x33, ';', '.'), (0x34, ':', '/'), (0x35, '!', '§'),
(0x01, '\x1B', '\x1B'),
(0x02, '&', '1'),
(0x03, 'é', '2'),
(0x04, '"', '3'),
(0x05, '\'', '4'),
(0x06, '(', '5'),
(0x07, '-', '6'),
(0x08, 'è', '7'),
(0x09, '_', '8'),
(0x0A, 'ç', '9'),
(0x0B, 'à', '0'),
(0x0C, ')', '°'),
(0x0D, '=', '+'),
(0x0E, '\x7F', '\x7F'),
(0x0F, '\t', '\t'),
(0x10, 'a', 'A'),
(0x11, 'z', 'Z'),
(0x12, 'e', 'E'),
(0x13, 'r', 'R'),
(0x14, 't', 'T'),
(0x15, 'y', 'Y'),
(0x16, 'u', 'U'),
(0x17, 'i', 'I'),
(0x18, 'o', 'O'),
(0x19, 'p', 'P'),
(0x1A, '^', '¨'),
(0x1B, '$', '£'),
(0x1C, '\n', '\n'),
(0x1E, 'q', 'Q'),
(0x1F, 's', 'S'),
(0x20, 'd', 'D'),
(0x21, 'f', 'F'),
(0x22, 'g', 'G'),
(0x23, 'h', 'H'),
(0x24, 'j', 'J'),
(0x25, 'k', 'K'),
(0x26, 'l', 'L'),
(0x27, 'm', 'M'),
(0x28, 'ù', '%'),
(0x29, '²', '~'),
(0x2B, '*', 'µ'),
(0x2C, 'w', 'W'),
(0x2D, 'x', 'X'),
(0x2E, 'c', 'C'),
(0x2F, 'v', 'V'),
(0x30, 'b', 'B'),
(0x31, 'n', 'N'),
(0x32, ',', '?'),
(0x33, ';', '.'),
(0x34, ':', '/'),
(0x35, '!', '§'),
(0x39, ' ', ' '),
];
for &(sc, normal, shifted) in bindings {
entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' });
entries.insert(
sc,
KeymapEntry {
scancode: sc,
normal,
shifted,
altgr: '\0',
altgr_shifted: '\0',
},
);
}
let mut dead_keys = Self::common_dead_keys();
dead_keys.push(DeadKeyEntry { dead_key: '^', base: ' ', composed: '^' });
dead_keys.push(DeadKeyEntry { dead_key: '¨', base: ' ', composed: '¨' });
Keymap { name: "azerty".into(), entries, compose: Vec::new(), dead_keys }
dead_keys.push(DeadKeyEntry {
dead_key: '^',
base: ' ',
composed: '^',
});
dead_keys.push(DeadKeyEntry {
dead_key: '¨',
base: ' ',
composed: '¨',
});
Keymap {
name: "azerty".into(),
entries,
compose: Vec::new(),
dead_keys,
}
}
fn make_bepo() -> Keymap {
let mut entries = HashMap::new();
let bindings: &[(u8, char, char)] = &[
(0x01, '\x1B', '\x1B'), (0x02, '"', '1'), (0x03, '«', '2'),
(0x04, '»', '3'), (0x05, '(', '4'), (0x06, ')', '5'),
(0x07, '@', '6'), (0x08, '+', '7'), (0x09, '-', '8'),
(0x0A, '/', '9'), (0x0B, '*', '0'), (0x0C, '=', '°'),
(0x0D, '%', '`'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
(0x10, 'b', 'B'), (0x11, 'é', 'É'), (0x12, 'p', 'P'),
(0x13, 'o', 'O'), (0x14, 'è', 'È'), (0x15, '^', '!'),
(0x16, 'v', 'V'), (0x17, 'd', 'D'), (0x18, 'l', 'L'),
(0x19, 'j', 'J'), (0x1A, 'z', 'Z'), (0x1B, 'w', 'W'),
(0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 'u', 'U'),
(0x20, 'i', 'I'), (0x21, 'e', 'E'), (0x22, ',', ';'),
(0x23, 'c', 'C'), (0x24, 't', 'T'), (0x25, 's', 'S'),
(0x26, 'r', 'R'), (0x27, 'n', 'N'), (0x28, 'm', 'M'),
(0x29, 'ç', 'Ç'), (0x2B, '\'', '?'), (0x2C, 'x', 'X'),
(0x2D, 'f', 'F'), (0x2E, 'h', 'H'), (0x2F, 'q', 'Q'),
(0x30, 'g', 'G'), (0x31, 'y', 'Y'), (0x32, 'k', 'K'),
(0x33, '.', ':'), (0x34, '/', '§'), (0x35, 'g', 'G'),
(0x01, '\x1B', '\x1B'),
(0x02, '"', '1'),
(0x03, '«', '2'),
(0x04, '»', '3'),
(0x05, '(', '4'),
(0x06, ')', '5'),
(0x07, '@', '6'),
(0x08, '+', '7'),
(0x09, '-', '8'),
(0x0A, '/', '9'),
(0x0B, '*', '0'),
(0x0C, '=', '°'),
(0x0D, '%', '`'),
(0x0E, '\x7F', '\x7F'),
(0x0F, '\t', '\t'),
(0x10, 'b', 'B'),
(0x11, 'é', 'É'),
(0x12, 'p', 'P'),
(0x13, 'o', 'O'),
(0x14, 'è', 'È'),
(0x15, '^', '!'),
(0x16, 'v', 'V'),
(0x17, 'd', 'D'),
(0x18, 'l', 'L'),
(0x19, 'j', 'J'),
(0x1A, 'z', 'Z'),
(0x1B, 'w', 'W'),
(0x1C, '\n', '\n'),
(0x1E, 'a', 'A'),
(0x1F, 'u', 'U'),
(0x20, 'i', 'I'),
(0x21, 'e', 'E'),
(0x22, ',', ';'),
(0x23, 'c', 'C'),
(0x24, 't', 'T'),
(0x25, 's', 'S'),
(0x26, 'r', 'R'),
(0x27, 'n', 'N'),
(0x28, 'm', 'M'),
(0x29, 'ç', 'Ç'),
(0x2B, '\'', '?'),
(0x2C, 'x', 'X'),
(0x2D, 'f', 'F'),
(0x2E, 'h', 'H'),
(0x2F, 'q', 'Q'),
(0x30, 'g', 'G'),
(0x31, 'y', 'Y'),
(0x32, 'k', 'K'),
(0x33, '.', ':'),
(0x34, '/', '§'),
(0x35, 'g', 'G'),
(0x39, ' ', ' '),
];
for &(sc, normal, shifted) in bindings {
entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' });
entries.insert(
sc,
KeymapEntry {
scancode: sc,
normal,
shifted,
altgr: '\0',
altgr_shifted: '\0',
},
);
}
let mut dead_keys = Self::common_dead_keys();
dead_keys.push(DeadKeyEntry { dead_key: '^', base: ' ', composed: '^' });
Keymap { name: "bepo".into(), entries, compose: Vec::new(), dead_keys }
dead_keys.push(DeadKeyEntry {
dead_key: '^',
base: ' ',
composed: '^',
});
Keymap {
name: "bepo".into(),
entries,
compose: Vec::new(),
dead_keys,
}
}
fn make_it() -> Keymap {
let mut km = Self::make_us();
km.name = "it".into();
if let Some(e) = km.entries.get_mut(&0x29) { e.normal = '\\'; e.shifted = '|'; }
if let Some(e) = km.entries.get_mut(&0x0C) { e.normal = '\''; e.shifted = '?'; }
if let Some(e) = km.entries.get_mut(&0x0D) { e.normal = 'ì'; e.shifted = '^'; }
if let Some(e) = km.entries.get_mut(&0x1A) { e.normal = 'è'; e.shifted = 'é'; }
if let Some(e) = km.entries.get_mut(&0x1B) { e.normal = '+'; e.shifted = '*'; }
if let Some(e) = km.entries.get_mut(&0x27) { e.normal = 'ò'; e.shifted = 'ç'; }
if let Some(e) = km.entries.get_mut(&0x28) { e.normal = 'à'; e.shifted = '°'; }
if let Some(e) = km.entries.get_mut(&0x2B) { e.normal = 'ù'; e.shifted = '§'; }
if let Some(e) = km.entries.get_mut(&0x29) {
e.normal = '\\';
e.shifted = '|';
}
if let Some(e) = km.entries.get_mut(&0x0C) {
e.normal = '\'';
e.shifted = '?';
}
if let Some(e) = km.entries.get_mut(&0x0D) {
e.normal = 'ì';
e.shifted = '^';
}
if let Some(e) = km.entries.get_mut(&0x1A) {
e.normal = 'è';
e.shifted = 'é';
}
if let Some(e) = km.entries.get_mut(&0x1B) {
e.normal = '+';
e.shifted = '*';
}
if let Some(e) = km.entries.get_mut(&0x27) {
e.normal = 'ò';
e.shifted = 'ç';
}
if let Some(e) = km.entries.get_mut(&0x28) {
e.normal = 'à';
e.shifted = '°';
}
if let Some(e) = km.entries.get_mut(&0x2B) {
e.normal = 'ù';
e.shifted = '§';
}
km
}
}
@@ -4,8 +4,6 @@ mod xkb;
use std::env;
use std::io::Write;
use std::thread;
use std::time::Duration;
use scheme::KeymapScheme;
@@ -24,13 +22,33 @@ fn main() {
Err(_) => "/etc/keymaps".to_string(),
};
if let Err(e) = scheme.load_from_dir(&keymap_dir) {
log_msg("ERROR", &format!("failed to load keymaps from {}: {}", keymap_dir, e));
log_msg(
"ERROR",
&format!("failed to load keymaps from {}: {}", keymap_dir, e),
);
}
log_msg("INFO", &format!("loaded {} keymap(s)", scheme.keymap_count()));
if let Ok(xkb_root) = env::var("XKB_CONFIG_ROOT") {
let layout = env::var("XKB_DEFAULT_LAYOUT").unwrap_or_else(|_| "us".to_string());
let variant = env::var("XKB_DEFAULT_VARIANT").ok();
if let Err(e) = scheme.load_xkb(&xkb_root, &layout, variant.as_deref()) {
log_msg(
"ERROR",
&format!(
"failed to load XKB keymap {} from {}: {}",
layout, xkb_root, e
),
);
}
}
let socket = redox_scheme::Socket::nonblock("keymap")
.expect("keymapd: failed to register scheme:keymap");
log_msg(
"INFO",
&format!("loaded {} keymap(s)", scheme.keymap_count()),
);
let socket =
redox_scheme::Socket::create("keymap").expect("keymapd: failed to register scheme:keymap");
log_msg("INFO", "registered scheme:keymap");
loop {
@@ -41,15 +59,16 @@ fn main() {
break;
}
Err(e) => {
log_msg("WARN", &format!("scheme read error (ignoring): {}", e));
thread::sleep(Duration::from_millis(100));
continue;
log_msg("ERROR", &format!("scheme read error: {}", e));
break;
}
};
match request.handle_scheme_block_mut(&mut scheme) {
Ok(response) => {
if let Err(e) = socket.write_response(response, redox_scheme::SignalBehavior::Restart) {
if let Err(e) =
socket.write_response(response, redox_scheme::SignalBehavior::Restart)
{
log_msg("ERROR", &format!("failed to write response: {}", e));
}
}
@@ -7,6 +7,7 @@ use syscall::flag::{MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
use crate::keymap::Keymap;
#[derive(Clone)]
enum HandleKind {
Root,
Active,
@@ -69,7 +70,12 @@ impl KeymapScheme {
Ok(())
}
pub fn load_xkb(&mut self, xkb_dir: &str, layout: &str, variant: Option<&str>) -> io::Result<()> {
pub fn load_xkb(
&mut self,
xkb_dir: &str,
layout: &str,
variant: Option<&str>,
) -> io::Result<()> {
let km = crate::xkb::load_xkb_keymap(xkb_dir, layout, variant)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let name = match variant {
@@ -136,9 +142,12 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme {
}
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let (kind, offset) = {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
(handle.kind.clone(), handle.offset)
};
let content: Vec<u8> = match &handle.kind {
let content: Vec<u8> = match &kind {
HandleKind::Root => {
let mut listing = String::new();
listing.push_str("active\nlist\n");
@@ -147,7 +156,12 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme {
}
listing.into_bytes()
}
HandleKind::Active => self.active_keymap.clone().into_bytes(),
HandleKind::Active => format!(
"name={}\nsample_scancode_30={}\n",
self.active_keymap,
self.translate(0x1E, false, false),
)
.into_bytes(),
HandleKind::List => {
let mut listing = String::new();
for (i, name) in self.keymaps.keys().enumerate() {
@@ -162,20 +176,25 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme {
HandleKind::Keymap { name } => {
let km = self.keymaps.get(name).ok_or(Error::new(ENOENT))?;
format!(
"name={}\nentries={}\ncompose={}\ndead_keys={}\n",
"name={}\nentries={}\ncompose={}\ndead_keys={}\nsample_scancode_30={}\nsample_altgr_scancode_30={}\nsample_compose_a_acute={}\nsample_compose_sequence={}\n",
km.name,
km.entries.len(),
km.compose.len(),
km.dead_keys.len()
km.dead_keys.len(),
km.get_char(0x1E, false, false),
km.get_char(0x1E, false, true),
km.compose('a', '\''),
km.lookup_compose("compose").unwrap_or('\0'),
)
.into_bytes()
}
};
if handle.offset >= content.len() {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
if offset >= content.len() {
return Ok(Some(0));
}
let remaining = &content[handle.offset..];
let remaining = &content[offset..];
let to_copy = remaining.len().min(buf.len());
buf[..to_copy].copy_from_slice(&remaining[..to_copy]);
handle.offset += to_copy;
@@ -210,10 +210,22 @@ struct XkbKeyEntry {
}
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');
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)
}
@@ -242,15 +254,15 @@ pub fn parse_xkb_symbols(content: &str, variant: Option<&str>) -> Result<Keymap,
}
if inner.starts_with("key <") {
if let Some(entry) = parse_key_line(inner) {
entries.entry(entry.scancode).or_insert_with(|| {
KeymapEntry {
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;
@@ -260,7 +272,10 @@ pub fn parse_xkb_symbols(content: &str, variant: Option<&str>) -> Result<Keymap,
}
if !found_variant {
return Err(format!("variant '{}' not found in XKB symbols file", target_variant));
return Err(format!(
"variant '{}' not found in XKB symbols file",
target_variant
));
}
Ok(Keymap {
@@ -316,7 +331,11 @@ fn parse_key_line(line: &str) -> Option<XkbKeyEntry> {
})
}
pub fn load_xkb_keymap(xkb_dir: &str, layout: &str, variant: Option<&str>) -> Result<Keymap, String> {
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))?;