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:
@@ -0,0 +1,221 @@
|
|||||||
|
# Relibc vs GNU libc — Cross-Reference Assessment
|
||||||
|
|
||||||
|
**Date:** 2026-05-05
|
||||||
|
**Reference:** glibc 2.41 (2026-05-05 clone from sourceware.org)
|
||||||
|
**Relibc pinned:** commit 861bbb0 with Red Bear patch chain (26 patches)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. eventfd
|
||||||
|
|
||||||
|
### glibc reference
|
||||||
|
|
||||||
|
```c
|
||||||
|
// sysdeps/unix/sysv/linux/eventfd.c (not cloned yet — syscall wrapper)
|
||||||
|
// bits/eventfd.h:
|
||||||
|
EFD_SEMAPHORE = 00000001 // octal 1
|
||||||
|
EFD_CLOEXEC = 02000000 // octal 0x80000
|
||||||
|
EFD_NONBLOCK = 00004000 // octal 0x800
|
||||||
|
```
|
||||||
|
|
||||||
|
glibc calls `INLINE_SYSCALL(eventfd2, 2, initval, flags)` — a kernel syscall. The kernel creates an anonymous file descriptor for event notification. Supports `EFD_SEMAPHORE` (semaphore-like counting), `EFD_CLOEXEC`, `EFD_NONBLOCK`.
|
||||||
|
|
||||||
|
### relibc current state
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/header/sys_eventfd/mod.rs — 8 lines
|
||||||
|
pub const EFD_SEMAPHORE: c_int = 1;
|
||||||
|
pub const EFD_CLOEXEC: c_int = 0x80000;
|
||||||
|
pub const EFD_NONBLOCK: c_int = 0x800;
|
||||||
|
// No eventfd() function — constants only
|
||||||
|
```
|
||||||
|
|
||||||
|
**Constants match** ✅
|
||||||
|
**No implementation** ❌ — libwayland provides inline `eventfd()` via `/scheme/event`
|
||||||
|
|
||||||
|
### Gaps
|
||||||
|
|
||||||
|
| Gap | Severity | Detail |
|
||||||
|
|-----|----------|--------|
|
||||||
|
| No `eventfd()` in relibc | Medium | libwayland has its own inline, but relibc should be canonical |
|
||||||
|
| No `eventfd_read()`/`eventfd_write()` | Low | POSIX-adjacent convenience wrappers (glibc provides them) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. signalfd
|
||||||
|
|
||||||
|
### glibc reference
|
||||||
|
|
||||||
|
```c
|
||||||
|
// sysdeps/unix/sysv/linux/signalfd.c
|
||||||
|
int signalfd(int fd, const sigset_t *mask, int flags) {
|
||||||
|
return INLINE_SYSCALL(signalfd4, 4, fd, mask, __NSIG_BYTES, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bits/signalfd.h:
|
||||||
|
SFD_CLOEXEC = 02000000 // octal 0x80000
|
||||||
|
SFD_NONBLOCK = 00004000 // octal 0x800
|
||||||
|
```
|
||||||
|
|
||||||
|
glibc is a thin syscall wrapper. Kernel handles signal mask, fd management, and non-blocking reads returning `struct signalfd_siginfo`.
|
||||||
|
|
||||||
|
### relibc current state
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/header/signal/signalfd.rs — 103 lines
|
||||||
|
// Full implementation: opens /scheme/event, applies CLOEXEC/NONBLOCK via fcntl,
|
||||||
|
// calls sigprocmask(SIG_BLOCK, mask), returns fd.
|
||||||
|
// signalfd4 supports modifying existing fd's flags.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flags match glibc** ✅
|
||||||
|
**signalfd_siginfo struct matches** ✅
|
||||||
|
**signalfd4 with existing fd** ✅ (fcntl-based flag modification)
|
||||||
|
|
||||||
|
### Prowess vs glibc
|
||||||
|
|
||||||
|
| Aspect | glibc | relibc |
|
||||||
|
|--------|-------|--------|
|
||||||
|
| Implementation | Syscall wrapper (5 lines) | Userspace via `/scheme/event` (100 lines) |
|
||||||
|
| Existing fd support | Kernel handles | fcntl O_CLOEXEC/O_NONBLOCK modification ✅ |
|
||||||
|
| Errno mapping | Kernel errno | Wraps in Errno, proper EINVAL/EFAULT |
|
||||||
|
| Signal blocking | Kernel auto-blocks on read | `sigprocmask(SIG_BLOCK, mask)` called explicitly ✅ |
|
||||||
|
|
||||||
|
### Gaps
|
||||||
|
|
||||||
|
| Gap | Severity | Detail |
|
||||||
|
|-----|----------|--------|
|
||||||
|
| No read path | High | Nothing reads `signalfd_siginfo` from the fd — the `/scheme/event` fd is opened but signals aren't delivered through it |
|
||||||
|
| Signal delivery unverified | High | The `sigprocmask(SIG_BLOCK)` blocks signals but there's no evidence the kernel delivers them via the event fd |
|
||||||
|
| `signalfd_siginfo` read not implemented | Critical | `struct signalfd_siginfo` is defined but never populated via read(2) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Semaphores
|
||||||
|
|
||||||
|
### glibc reference
|
||||||
|
|
||||||
|
```c
|
||||||
|
// sysdeps/pthread/sem_open.c — 216 lines
|
||||||
|
// Uses:
|
||||||
|
// __shm_get_name(name) → canonical path in /dev/shm
|
||||||
|
// O_CREAT+O_EXCL path: creates temp file, writes semaphore header, ftruncate to sizeof(sem_t), mmap
|
||||||
|
// Non-create path: open existing, __sem_check_add_mapping(name, fd) → reuse or mmap
|
||||||
|
// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE) — cancellation-safe
|
||||||
|
// va_arg for mode_t when O_CREAT
|
||||||
|
```
|
||||||
|
|
||||||
|
glibc uses a sophisticated named semaphore implementation:
|
||||||
|
1. **Name canonicalization**: `__shm_get_name` transforms `/name` → `/dev/shm/sem.name`
|
||||||
|
2. **Existing mapping reuse**: `__sem_check_add_mapping` checks global list of already-mapped semaphores
|
||||||
|
3. **Atomic creation**: O_CREAT+O_EXCL with temp file, then writes header, ftruncate, mmap
|
||||||
|
4. **Cancellation safety**: `pthread_setcancelstate(PTHREAD_CANCEL_DISABLE)` around file operations
|
||||||
|
5. **Proper mode_t**: va_arg for mode when O_CREAT
|
||||||
|
6. **Reference counting**: `__sem_check_add_mapping` increments refcount, `sem_close` decrements
|
||||||
|
|
||||||
|
### relibc current state
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/header/semaphore/mod.rs — 176 lines
|
||||||
|
// Uses: shm_open(name, O_CREAT|O_EXCL|O_RDWR, mode) → ftruncate → mmap → init
|
||||||
|
// NamedSemaphore struct with RlctSempahore (futex-based)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Core mechanism works** ✅ (shm_open + mmap)
|
||||||
|
**sem_init/destroy/post/wait/trywait/timedwait/clockwait** ✅
|
||||||
|
**sem_open/close/unlink** ✅ (implemented in P3-semaphore-comprehensive.patch)
|
||||||
|
|
||||||
|
### Gaps vs glibc
|
||||||
|
|
||||||
|
| Gap | Severity | Detail |
|
||||||
|
|-----|----------|--------|
|
||||||
|
| **No name canonicalization** | Medium | Names go directly to `shm_open` without prefix/suffix. glibc uses `/dev/shm/sem.NAME` |
|
||||||
|
| **No existing mapping reuse** | High | Every `sem_open` creates a NEW mmap even if already open. Wasteful and races with `sem_close` on shared references |
|
||||||
|
| **No refcounting** | High | `sem_close` unconditionally munmaps. If two threads open the same semaphore, one close breaks the other |
|
||||||
|
| **No cancellation safety** | Low | No `pthread_setcancelstate` around file ops |
|
||||||
|
| **va_list not parsed** | Medium | `sem_open` hardcodes `value=0` when O_CREAT, ignoring mode and initial value from varargs |
|
||||||
|
| **No `__sem_check_add_mapping` equivalent** | High | Opens named sem every time instead of reusing existing mapping |
|
||||||
|
| **No O_NOFOLLOW** | Low | glibc uses `O_NOFOLLOW` for security |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Cross-Cutting Gaps
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
| Area | glibc | relibc |
|
||||||
|
|------|-------|--------|
|
||||||
|
| errno thread-safety | TLS errno via kernel | `Cell<c_int>` per platform ✅ |
|
||||||
|
| errno after close | Preserved (close may overwrite) | `let _ = Sys::close(fd)` — ignores errors ✅ |
|
||||||
|
| EINTR | Handled in syscall wrappers | Not consistently handled ⚠️ |
|
||||||
|
|
||||||
|
### Signal Safety
|
||||||
|
|
||||||
|
| Function | glibc | relibc |
|
||||||
|
|----------|-------|--------|
|
||||||
|
| `sem_post` | AS-safe (futex) | AS-safe ✅ |
|
||||||
|
| `sem_wait` | AS-safe (futex wait, EINTR) | No EINTR handling ⚠️ |
|
||||||
|
| `eventfd` write/read | AS-safe | Not implemented |
|
||||||
|
|
||||||
|
### Thread Safety
|
||||||
|
|
||||||
|
| Area | glibc | relibc |
|
||||||
|
|------|-------|--------|
|
||||||
|
| sem_open refcount | Mutex-protected global list | None — race on same name ⚠️ |
|
||||||
|
| sem_close/sem_unlink | Mutex-protected | None — concurrent close races ⚠️ |
|
||||||
|
| signalfd mask | Per-process (kernel) | Per-call sigprocmask ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Priority Improvement Plan
|
||||||
|
|
||||||
|
### Phase S1: Critical Correctness (1-2 weeks)
|
||||||
|
|
||||||
|
1. **sem_open refcounting** — Add global `HashMap<String, (Arc<NamedSemaphore>, AtomicUsize)>` to reuse existing mappings. `sem_close` decrements refcount, munmaps only when zero.
|
||||||
|
2. **Eventfd implementation** — Implement `eventfd()` via `/scheme/event/eventfd/` using the existing scheme mechanism. Remove libwayland's inline copy.
|
||||||
|
|
||||||
|
### Phase S2: Completeness (2-3 weeks)
|
||||||
|
|
||||||
|
3. **signalfd read path** — Implement read(2) → `signalfd_siginfo` struct population. The `/scheme/event` fd must deliver signal info formatted as `signalfd_siginfo`.
|
||||||
|
4. **sem_open va_list** — Parse `mode_t` and `value` from varargs when O_CREAT. Requires `crate::header::stdarg` or manual stack walking.
|
||||||
|
5. **sem_open name canonicalization** — Prefix names with `/scheme/shm/sem.` for namespace isolation.
|
||||||
|
|
||||||
|
### Phase S3: Robustness (3-4 weeks)
|
||||||
|
|
||||||
|
6. **EINTR handling** — Wrap futex waits to retry on `EINTR`.
|
||||||
|
7. **sem_open cancellation safety** — Add `pthread_setcancelstate` around file ops (if pthread cancellation is supported).
|
||||||
|
8. **eventfd semaphore mode** — Implement `EFD_SEMAPHORE` counting semantics (decrements on read, blocks at 0).
|
||||||
|
|
||||||
|
### Phase S4: POSIX Conformance (2-3 weeks)
|
||||||
|
|
||||||
|
9. **eventfd_read/eventfd_write** — Convenience wrappers.
|
||||||
|
10. **sem_open existing-semaphore reopening** — Handle `(oflag & O_CREAT) == 0` path (open existing without O_EXCL).
|
||||||
|
11. **Signalfd signal delivery verification** — Runtime tests proving signals arrive via signalfd.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Eventfd Kernel Requirement
|
||||||
|
|
||||||
|
Unlike signalfd and sem_open which can be implemented in userspace via existing schemes (`/scheme/event`, `shm_open`), eventfd currently relies on libwayland's inline implementation that opens `/scheme/event/eventfd/`. A canonical relibc implementation should use the same path.
|
||||||
|
|
||||||
|
The `/scheme/event` kernel scheme needs:
|
||||||
|
- Support for `eventfd` sub-path
|
||||||
|
- EFD_SEMAPHORE counting semantics in kernel
|
||||||
|
- Non-blocking reads returning `u64` count
|
||||||
|
|
||||||
|
This is a **kernel change** and should be tracked separately from relibc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Summary
|
||||||
|
|
||||||
|
| Component | relibc Status | Matches glibc | Critical Gaps |
|
||||||
|
|-----------|--------------|---------------|---------------|
|
||||||
|
| eventfd | Constants only | Constants ✅ | No function ❌, no EFD_SEMAPHORE support |
|
||||||
|
| signalfd | 103-line impl | Flags ✅, struct ✅, signalfd4 ✅ | No read path ❌ (signals not delivered via fd) |
|
||||||
|
| sem_open | 176-line impl | Core works ✅, shm+mmap ✅ | No refcounting ❌, no va_list ❌, no mapping reuse |
|
||||||
|
| sem_close | Simple munmap | Semantics correct | No refcounting creates race |
|
||||||
|
| sem_wait/post | Futex-based | Works ✅ | No EINTR handling ⚠️ |
|
||||||
|
|
||||||
|
**Total estimated effort: 8-12 weeks for all gaps.**
|
||||||
|
**Critical path: eventfd kernal + signalfd read + sem_open refcounting (Phase S1).**
|
||||||
@@ -1,43 +1,115 @@
|
|||||||
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
||||||
index 9f507221..c57d91dc 100644
|
|
||||||
--- a/daemon/src/lib.rs
|
--- a/daemon/src/lib.rs
|
||||||
+++ b/daemon/src/lib.rs
|
+++ b/daemon/src/lib.rs
|
||||||
@@ -11,12 +11,23 @@ use redox_scheme::Socket;
|
@@ -10,26 +10,35 @@ use libredox::Fd;
|
||||||
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
||||||
|
use redox_scheme::Socket;
|
||||||
|
|
||||||
unsafe fn get_fd(var: &str) -> RawFd {
|
-unsafe fn get_fd(var: &str) -> RawFd {
|
||||||
- let fd: RawFd = std::env::var(var).unwrap().parse().unwrap();
|
- let fd: RawFd = match std::env::var(var)
|
||||||
+ let fd: RawFd = match std::env::var(var)
|
- .map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
|
||||||
+ .map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
|
- .ok()
|
||||||
+ .ok()
|
- .and_then(|val| {
|
||||||
+ .and_then(|val| {
|
- val.parse()
|
||||||
+ val.parse()
|
- .map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}"))
|
||||||
+ .map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}"))
|
- .ok()
|
||||||
+ .ok()
|
- }) {
|
||||||
+ }) {
|
- Some(fd) => fd,
|
||||||
+ Some(fd) => fd,
|
- None => return -1,
|
||||||
+ None => return -1,
|
+unsafe fn get_fd(var: &str) -> Option<RawFd> {
|
||||||
|
+ let value = match std::env::var(var) {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(_) => {
|
||||||
|
+ let exe = std::env::args()
|
||||||
|
+ .next()
|
||||||
|
+ .unwrap_or_else(|| "daemon".to_string());
|
||||||
|
+ eprintln!("daemon: {var} not set for {exe}; readiness notification disabled");
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
+ };
|
+ };
|
||||||
|
+ let fd: RawFd = match value.parse() {
|
||||||
|
+ Ok(fd) => fd,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ let exe = std::env::args()
|
||||||
|
+ .next()
|
||||||
|
+ .unwrap_or_else(|| "daemon".to_string());
|
||||||
|
+ eprintln!("daemon: invalid {var} value {value:?} for {exe}: {err}; readiness notification disabled");
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
};
|
||||||
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
|
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
|
||||||
- panic!(
|
eprintln!(
|
||||||
+ eprintln!(
|
|
||||||
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
||||||
io::Error::last_os_error()
|
io::Error::last_os_error()
|
||||||
);
|
);
|
||||||
+ return -1;
|
- return -1;
|
||||||
|
+ return None;
|
||||||
}
|
}
|
||||||
fd
|
- fd
|
||||||
|
+ Some(fd)
|
||||||
}
|
}
|
||||||
@@ -51,7 +62,11 @@ impl Daemon {
|
|
||||||
|
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
||||||
|
@@ -47,22 +56,20 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
||||||
|
/// A long running background process that handles requests.
|
||||||
|
#[must_use = "Daemon::ready must be called"]
|
||||||
|
pub struct Daemon {
|
||||||
|
- write_pipe: PipeWriter,
|
||||||
|
+ write_pipe: Option<PipeWriter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Daemon {
|
||||||
|
/// Create a new daemon.
|
||||||
|
pub fn new(f: impl FnOnce(Daemon) -> !) -> ! {
|
||||||
|
- let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
|
||||||
|
+ let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
|
||||||
|
|
||||||
|
f(Daemon { write_pipe })
|
||||||
|
}
|
||||||
|
|
||||||
/// Notify the process that the daemon is ready to accept requests.
|
/// Notify the process that the daemon is ready to accept requests.
|
||||||
pub fn ready(mut self) {
|
pub fn ready(mut self) {
|
||||||
- self.write_pipe.write_all(&[0]).unwrap();
|
- if let Err(err) = self.write_pipe.write_all(&[0]) {
|
||||||
+ if let Err(err) = self.write_pipe.write_all(&[0]) {
|
- if err.kind() != io::ErrorKind::BrokenPipe {
|
||||||
+ if err.kind() != io::ErrorKind::BrokenPipe {
|
- eprintln!("daemon::ready write failed: {err}");
|
||||||
+ eprintln!("daemon::ready write failed: {err}");
|
- }
|
||||||
+ }
|
+ if let Some(write_pipe) = self.write_pipe.as_mut() {
|
||||||
+ }
|
+ write_pipe.write_all(&[0]).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `Command` as a child process.
|
@@ -87,24 +94,26 @@ impl Daemon {
|
||||||
|
/// A long running background process that handles requests using schemes.
|
||||||
|
#[must_use = "SchemeDaemon::ready must be called"]
|
||||||
|
pub struct SchemeDaemon {
|
||||||
|
- write_pipe: PipeWriter,
|
||||||
|
+ write_pipe: Option<PipeWriter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SchemeDaemon {
|
||||||
|
/// Create a new daemon for use with schemes.
|
||||||
|
pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! {
|
||||||
|
- let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
|
||||||
|
+ let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
|
||||||
|
|
||||||
|
f(SchemeDaemon { write_pipe })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the process that the scheme daemon is ready to accept requests.
|
||||||
|
pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> {
|
||||||
|
- syscall::call_wo(
|
||||||
|
- self.write_pipe.as_raw_fd() as usize,
|
||||||
|
- &cap_fd.into_raw().to_ne_bytes(),
|
||||||
|
- syscall::CallFlags::FD,
|
||||||
|
- &[],
|
||||||
|
- )?;
|
||||||
|
+ if let Some(write_pipe) = self.write_pipe {
|
||||||
|
+ syscall::call_wo(
|
||||||
|
+ write_pipe.as_raw_fd() as usize,
|
||||||
|
+ &cap_fd.into_raw().to_ne_bytes(),
|
||||||
|
+ syscall::CallFlags::FD,
|
||||||
|
+ &[],
|
||||||
|
+ )?;
|
||||||
|
+ }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,26 +1,17 @@
|
|||||||
diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml
|
|
||||||
--- a/src/header/semaphore/cbindgen.toml
|
|
||||||
+++ b/src/header/semaphore/cbindgen.toml
|
|
||||||
@@ -3,6 +3,9 @@ include_guard = "_RELIBC_SEMAPHORE_H"
|
|
||||||
after_includes = """
|
|
||||||
#include <bits/timespec.h> // for timespec
|
|
||||||
"""
|
|
||||||
+trailer = """
|
|
||||||
+#define SEM_FAILED ((sem_t *) -1)
|
|
||||||
+"""
|
|
||||||
language = "C"
|
|
||||||
style = "Type"
|
|
||||||
no_includes = true
|
|
||||||
diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
||||||
--- a/src/header/semaphore/mod.rs
|
--- a/src/header/semaphore/mod.rs
|
||||||
+++ b/src/header/semaphore/mod.rs
|
+++ b/src/header/semaphore/mod.rs
|
||||||
@@ -2,12 +2,23 @@
|
@@ -2,12 +2,27 @@
|
||||||
//!
|
//!
|
||||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||||
|
|
||||||
|
+use alloc::collections::BTreeMap;
|
||||||
|
+use alloc::vec::Vec;
|
||||||
+use core::ptr;
|
+use core::ptr;
|
||||||
|
+use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
+
|
+
|
||||||
use crate::{
|
use crate::{
|
||||||
|
+ c_str::CStr,
|
||||||
header::{
|
header::{
|
||||||
bits_timespec::timespec,
|
bits_timespec::timespec,
|
||||||
+ errno::EINVAL,
|
+ errno::EINVAL,
|
||||||
@@ -28,7 +19,6 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
|||||||
+ sys_mman::{
|
+ sys_mman::{
|
||||||
+ MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink,
|
+ MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink,
|
||||||
+ },
|
+ },
|
||||||
+ sys_stat::S_IRWXU,
|
|
||||||
time::{CLOCK_MONOTONIC, CLOCK_REALTIME},
|
time::{CLOCK_MONOTONIC, CLOCK_REALTIME},
|
||||||
},
|
},
|
||||||
- platform::types::{c_char, c_int, c_long, c_uint, clockid_t},
|
- platform::types::{c_char, c_int, c_long, c_uint, clockid_t},
|
||||||
@@ -36,10 +26,11 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
|||||||
+ Pal, Sys, ERRNO,
|
+ Pal, Sys, ERRNO,
|
||||||
+ types::{c_char, c_int, c_long, c_uint, clockid_t, mode_t},
|
+ types::{c_char, c_int, c_long, c_uint, clockid_t, mode_t},
|
||||||
+ },
|
+ },
|
||||||
|
+ sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||||
@@ -19,11 +30,57 @@ pub union sem_t {
|
@@ -19,11 +34,86 @@
|
||||||
pub align: c_long,
|
pub align: c_long,
|
||||||
}
|
}
|
||||||
pub type RlctSempahore = crate::sync::Semaphore;
|
pub type RlctSempahore = crate::sync::Semaphore;
|
||||||
@@ -50,6 +41,24 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
|||||||
+
|
+
|
||||||
+const SEM_FAILED_PTR: *mut sem_t = usize::MAX as *mut sem_t;
|
+const SEM_FAILED_PTR: *mut sem_t = usize::MAX as *mut sem_t;
|
||||||
+
|
+
|
||||||
|
+struct NamedSemEntry {
|
||||||
|
+ ptr: *mut NamedSemaphore,
|
||||||
|
+ size: usize,
|
||||||
|
+ refs: AtomicUsize,
|
||||||
|
+ unlinked: bool,
|
||||||
|
+}
|
||||||
|
+unsafe impl Send for NamedSemEntry {}
|
||||||
|
+
|
||||||
|
+static NAMED_SEMS: Mutex<Option<BTreeMap<Vec<u8>, NamedSemEntry>>> = Mutex::new(None);
|
||||||
|
+
|
||||||
|
+fn with_named_sems<R>(f: impl FnOnce(&mut BTreeMap<Vec<u8>, NamedSemEntry>) -> R) -> R {
|
||||||
|
+ let mut guard = NAMED_SEMS.lock();
|
||||||
|
+ if guard.is_none() {
|
||||||
|
+ *guard = Some(BTreeMap::new());
|
||||||
|
+ }
|
||||||
|
+ f(guard.as_mut().unwrap())
|
||||||
|
+}
|
||||||
|
+
|
||||||
+unsafe fn map_named_semaphore(
|
+unsafe fn map_named_semaphore(
|
||||||
+ name: *const c_char,
|
+ name: *const c_char,
|
||||||
+ oflag: c_int,
|
+ oflag: c_int,
|
||||||
@@ -91,14 +100,25 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
|||||||
+ return -1;
|
+ return -1;
|
||||||
+ }
|
+ }
|
||||||
+ let named_ptr = sem as *mut NamedSemaphore;
|
+ let named_ptr = sem as *mut NamedSemaphore;
|
||||||
+ if unsafe { munmap(named_ptr as *mut crate::platform::types::c_void, core::mem::size_of::<NamedSemaphore>()) } != 0 {
|
+ let mut should_unmap = false;
|
||||||
+ return -1;
|
+ with_named_sems(|map| {
|
||||||
|
+ let key = map.iter().find(|(_, e)| e.ptr == named_ptr).map(|(k, _)| k.clone());
|
||||||
|
+ if let Some(key) = key {
|
||||||
|
+ let entry = map.get(&key).unwrap();
|
||||||
|
+ let prev = entry.refs.fetch_sub(1, Ordering::Release);
|
||||||
|
+ if prev == 1 { should_unmap = true; map.remove(&key); }
|
||||||
|
+ } else { should_unmap = true; }
|
||||||
|
+ });
|
||||||
|
+ if should_unmap {
|
||||||
|
+ if unsafe { munmap(named_ptr as *mut crate::platform::types::c_void, core::mem::size_of::<NamedSemaphore>()) } != 0 {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ 0
|
+ 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_destroy.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_destroy.html>.
|
||||||
@@ -50,13 +107,13 @@ pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uin
|
@@ -50,13 +140,31 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.html>.
|
||||||
@@ -107,15 +127,34 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
|||||||
+#[unsafe(no_mangle)]
|
+#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn sem_open(
|
pub unsafe extern "C" fn sem_open(
|
||||||
name: *const c_char,
|
name: *const c_char,
|
||||||
oflag: c_int, /* (va_list) value: c_uint */
|
- oflag: c_int, /* (va_list) value: c_uint */
|
||||||
|
+ oflag: c_int, /* (va_list) mode: mode_t, value: c_uint */
|
||||||
) -> *mut sem_t {
|
) -> *mut sem_t {
|
||||||
- todo!("named semaphores")
|
- todo!("named semaphores")
|
||||||
+ let value: c_uint = 0;
|
+ if name.is_null() { ERRNO.set(EINVAL); return SEM_FAILED_PTR; }
|
||||||
+ unsafe { map_named_semaphore(name, oflag, S_IRWXU as mode_t, value) }
|
+ let name_bytes = unsafe { CStr::from_ptr(name) }.to_bytes().to_vec();
|
||||||
|
+ let create = (oflag & O_CREAT) != 0;
|
||||||
|
+ let excl = (oflag & O_EXCL) != 0;
|
||||||
|
+ let existing = with_named_sems(|map| map.get(&name_bytes).map(|e| e.ptr));
|
||||||
|
+ if let Some(ptr) = existing {
|
||||||
|
+ if excl { ERRNO.set(crate::header::errno::EEXIST); return SEM_FAILED_PTR; }
|
||||||
|
+ with_named_sems(|map| { if let Some(e) = map.get(&name_bytes) { e.refs.fetch_add(1, Ordering::Relaxed); } });
|
||||||
|
+ return ptr as *mut NamedSemaphore as *mut sem_t;
|
||||||
|
+ }
|
||||||
|
+ let (mode, value): (mode_t, c_uint) = {
|
||||||
|
+ let oflag_ptr: *const c_int = &oflag;
|
||||||
|
+ let mode_ptr = unsafe { oflag_ptr.add(1) as *const mode_t };
|
||||||
|
+ let value_ptr = unsafe { oflag_ptr.add(2) as *const c_uint };
|
||||||
|
+ (unsafe { *mode_ptr }, if create { unsafe { *value_ptr } } else { 0 })
|
||||||
|
+ };
|
||||||
|
+ let ptr = unsafe { map_named_semaphore(name, oflag, mode, value) };
|
||||||
|
+ if ptr == SEM_FAILED_PTR { return SEM_FAILED_PTR; }
|
||||||
|
+ with_named_sems(|map| { map.insert(name_bytes, NamedSemEntry { ptr: ptr as *mut NamedSemaphore, size: core::mem::size_of::<NamedSemaphore>(), refs: AtomicUsize::new(1), unlinked: false }); });
|
||||||
|
+ ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_post.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_post.html>.
|
||||||
@@ -76,9 +133,13 @@ pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int {
|
@@ -76,9 +184,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_unlink.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_unlink.html>.
|
||||||
@@ -123,12 +162,26 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
|||||||
+#[unsafe(no_mangle)]
|
+#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int {
|
pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int {
|
||||||
- todo!("named semaphores")
|
- todo!("named semaphores")
|
||||||
+ if name.is_null() {
|
+ if name.is_null() { ERRNO.set(EINVAL); return -1; }
|
||||||
+ ERRNO.set(EINVAL);
|
+ let name_bytes = unsafe { CStr::from_ptr(name) }.to_bytes().to_vec();
|
||||||
+ return -1;
|
+ with_named_sems(|map| { if let Some(e) = map.get_mut(&name_bytes) { e.unlinked = true; } });
|
||||||
+ }
|
|
||||||
+ unsafe { shm_unlink(name) }
|
+ unsafe { shm_unlink(name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_trywait.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_trywait.html>.
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml
|
||||||
|
--- a/src/header/semaphore/cbindgen.toml
|
||||||
|
+++ b/src/header/semaphore/cbindgen.toml
|
||||||
|
@@ -3,6 +3,9 @@
|
||||||
|
after_includes = """
|
||||||
|
#include <bits/timespec.h> // for timespec
|
||||||
|
"""
|
||||||
|
+trailer = """
|
||||||
|
+#define SEM_FAILED ((sem_t *) -1)
|
||||||
|
+"""
|
||||||
|
language = "C"
|
||||||
|
style = "Type"
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
mod scheme;
|
mod scheme;
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use scheme::AccessibilityScheme;
|
use scheme::AccessibilityScheme;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
fn log_msg(level: &str, msg: &str) {
|
fn log_msg(level: &str, msg: &str) {
|
||||||
let _ = writeln!(std::io::stderr(), "[accessibility] {} {}", level, msg);
|
let _ = writeln!(std::io::stderr(), "[accessibility] {} {}", level, msg);
|
||||||
@@ -13,7 +10,7 @@ fn log_msg(level: &str, msg: &str) {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut scheme = AccessibilityScheme::new();
|
let mut scheme = AccessibilityScheme::new();
|
||||||
|
|
||||||
let socket = redox_scheme::Socket::nonblock("accessibility")
|
let socket = redox_scheme::Socket::create("accessibility")
|
||||||
.expect("accessibility: failed to register scheme:accessibility");
|
.expect("accessibility: failed to register scheme:accessibility");
|
||||||
log_msg("INFO", "registered scheme:accessibility");
|
log_msg("INFO", "registered scheme:accessibility");
|
||||||
|
|
||||||
@@ -25,15 +22,16 @@ fn main() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log_msg("WARN", &format!("scheme read error (ignoring): {}", e));
|
log_msg("ERROR", &format!("scheme read error: {}", e));
|
||||||
thread::sleep(Duration::from_millis(100));
|
break;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match request.handle_scheme_block_mut(&mut scheme) {
|
match request.handle_scheme_block_mut(&mut scheme) {
|
||||||
Ok(response) => {
|
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));
|
log_msg("ERROR", &format!("failed to write response: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT};
|
|||||||
use syscall::flag::{MODE_DIR, MODE_FILE};
|
use syscall::flag::{MODE_DIR, MODE_FILE};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum StickyKeyState {
|
pub(crate) enum StickyKeyState {
|
||||||
Off,
|
Off,
|
||||||
Latched,
|
Latched,
|
||||||
Locked,
|
Locked,
|
||||||
@@ -251,6 +251,29 @@ impl redox_scheme::SchemeBlockMut for AccessibilityScheme {
|
|||||||
}
|
}
|
||||||
Ok(Some(buf.len()))
|
Ok(Some(buf.len()))
|
||||||
}
|
}
|
||||||
|
HandleKind::StickyKeys => {
|
||||||
|
let input = String::from_utf8_lossy(buf);
|
||||||
|
for line in input.lines() {
|
||||||
|
let mut fields = line.split_whitespace();
|
||||||
|
let Some("key") = fields.next() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(code) = fields.next().and_then(|field| field.parse::<u16>().ok())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(pressed) = fields.next().and_then(|field| field.parse::<bool>().ok())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let now_ms = fields
|
||||||
|
.next()
|
||||||
|
.and_then(|field| field.parse::<u64>().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let _ = self.filter_key(code, pressed, now_ms);
|
||||||
|
}
|
||||||
|
Ok(Some(buf.len()))
|
||||||
|
}
|
||||||
_ => Err(Error::new(EINVAL)),
|
_ => Err(Error::new(EINVAL)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
mod scheme;
|
mod scheme;
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use scheme::ImeScheme;
|
use scheme::ImeScheme;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
fn log_msg(level: &str, msg: &str) {
|
fn log_msg(level: &str, msg: &str) {
|
||||||
let _ = writeln!(std::io::stderr(), "[ime] {} {}", level, msg);
|
let _ = writeln!(std::io::stderr(), "[ime] {} {}", level, msg);
|
||||||
@@ -13,8 +10,7 @@ fn log_msg(level: &str, msg: &str) {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut scheme = ImeScheme::new();
|
let mut scheme = ImeScheme::new();
|
||||||
|
|
||||||
let socket = redox_scheme::Socket::nonblock("ime")
|
let socket = redox_scheme::Socket::create("ime").expect("ime: failed to register scheme:ime");
|
||||||
.expect("ime: failed to register scheme:ime");
|
|
||||||
log_msg("INFO", "registered scheme:ime");
|
log_msg("INFO", "registered scheme:ime");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -25,15 +21,16 @@ fn main() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log_msg("WARN", &format!("scheme read error (ignoring): {}", e));
|
log_msg("ERROR", &format!("scheme read error: {}", e));
|
||||||
thread::sleep(Duration::from_millis(100));
|
break;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match request.handle_scheme_block_mut(&mut scheme) {
|
match request.handle_scheme_block_mut(&mut scheme) {
|
||||||
Ok(response) => {
|
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));
|
log_msg("ERROR", &format!("failed to write response: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,17 +93,50 @@ impl BasicLatinEngine {
|
|||||||
}
|
}
|
||||||
fn scancode_to_char(scancode: u8, shift: bool) -> Option<char> {
|
fn scancode_to_char(scancode: u8, shift: bool) -> Option<char> {
|
||||||
let lower = match scancode {
|
let lower = match scancode {
|
||||||
0x02 => '1', 0x03 => '2', 0x04 => '3', 0x05 => '4', 0x06 => '5',
|
0x02 => '1',
|
||||||
0x07 => '6', 0x08 => '7', 0x09 => '8', 0x0A => '9', 0x0B => '0',
|
0x03 => '2',
|
||||||
0x10 => 'q', 0x11 => 'w', 0x12 => 'e', 0x13 => 'r', 0x14 => 't',
|
0x04 => '3',
|
||||||
0x15 => 'y', 0x16 => 'u', 0x17 => 'i', 0x18 => 'o', 0x19 => 'p',
|
0x05 => '4',
|
||||||
0x1E => 'a', 0x1F => 's', 0x20 => 'd', 0x21 => 'f', 0x22 => 'g',
|
0x06 => '5',
|
||||||
0x23 => 'h', 0x24 => 'j', 0x25 => 'k', 0x26 => 'l',
|
0x07 => '6',
|
||||||
0x2C => 'z', 0x2D => 'x', 0x2E => 'c', 0x2F => 'v', 0x30 => 'b',
|
0x08 => '7',
|
||||||
0x31 => 'n', 0x32 => 'm',
|
0x09 => '8',
|
||||||
0x27 => ';', 0x28 => '\'', 0x29 => '`',
|
0x0A => '9',
|
||||||
0x33 => ',', 0x34 => '.', 0x35 => '/',
|
0x0B => '0',
|
||||||
0x0C => '-', 0x0D => '=',
|
0x10 => 'q',
|
||||||
|
0x11 => 'w',
|
||||||
|
0x12 => 'e',
|
||||||
|
0x13 => 'r',
|
||||||
|
0x14 => 't',
|
||||||
|
0x15 => 'y',
|
||||||
|
0x16 => 'u',
|
||||||
|
0x17 => 'i',
|
||||||
|
0x18 => 'o',
|
||||||
|
0x19 => 'p',
|
||||||
|
0x1E => 'a',
|
||||||
|
0x1F => 's',
|
||||||
|
0x20 => 'd',
|
||||||
|
0x21 => 'f',
|
||||||
|
0x22 => 'g',
|
||||||
|
0x23 => 'h',
|
||||||
|
0x24 => 'j',
|
||||||
|
0x25 => 'k',
|
||||||
|
0x26 => 'l',
|
||||||
|
0x2C => 'z',
|
||||||
|
0x2D => 'x',
|
||||||
|
0x2E => 'c',
|
||||||
|
0x2F => 'v',
|
||||||
|
0x30 => 'b',
|
||||||
|
0x31 => 'n',
|
||||||
|
0x32 => 'm',
|
||||||
|
0x27 => ';',
|
||||||
|
0x28 => '\'',
|
||||||
|
0x29 => '`',
|
||||||
|
0x33 => ',',
|
||||||
|
0x34 => '.',
|
||||||
|
0x35 => '/',
|
||||||
|
0x0C => '-',
|
||||||
|
0x0D => '=',
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
if shift {
|
if shift {
|
||||||
@@ -157,8 +190,10 @@ impl ImeEngine for BasicLatinEngine {
|
|||||||
let is_dead_trigger = scancode == self.compose_scancode;
|
let is_dead_trigger = scancode == self.compose_scancode;
|
||||||
if is_dead_trigger {
|
if is_dead_trigger {
|
||||||
let dead_keys = [
|
let dead_keys = [
|
||||||
('\u{0300}', "grave"), ('\u{0301}', "acute"),
|
('\u{0300}', "grave"),
|
||||||
('\u{0302}', "circumflex"), ('\u{0308}', "diaeresis"),
|
('\u{0301}', "acute"),
|
||||||
|
('\u{0302}', "circumflex"),
|
||||||
|
('\u{0308}', "diaeresis"),
|
||||||
('\u{030C}', "caron"),
|
('\u{030C}', "caron"),
|
||||||
];
|
];
|
||||||
let (dk, _name) = dead_keys[self.dead_key_index % dead_keys.len()];
|
let (dk, _name) = dead_keys[self.dead_key_index % dead_keys.len()];
|
||||||
@@ -206,6 +241,9 @@ impl ImeScheme {
|
|||||||
self.state.composing = false;
|
self.state.composing = false;
|
||||||
self.state.preedit.clear();
|
self.state.preedit.clear();
|
||||||
}
|
}
|
||||||
|
if result.candidates_changed {
|
||||||
|
self.state.composing = !self.engine.candidates().is_empty();
|
||||||
|
}
|
||||||
if !result.preedit.is_empty() {
|
if !result.preedit.is_empty() {
|
||||||
self.state.composing = true;
|
self.state.composing = true;
|
||||||
self.state.preedit = result.preedit;
|
self.state.preedit = result.preedit;
|
||||||
@@ -235,13 +273,11 @@ impl redox_scheme::SchemeBlockMut for ImeScheme {
|
|||||||
|
|
||||||
let content: Vec<u8> = match &handle.kind {
|
let content: Vec<u8> = match &handle.kind {
|
||||||
HandleKind::Root => "state\ncompose\ncandidates\n".as_bytes().to_vec(),
|
HandleKind::Root => "state\ncompose\ncandidates\n".as_bytes().to_vec(),
|
||||||
HandleKind::State => {
|
HandleKind::State => format!(
|
||||||
format!(
|
"composing={}\npreedit={}\ncommitted={}\n",
|
||||||
"composing={}\npreedit={}\ncommitted={}\n",
|
self.state.composing, self.state.preedit, self.state.committed
|
||||||
self.state.composing, self.state.preedit, self.state.committed
|
)
|
||||||
)
|
.into_bytes(),
|
||||||
.into_bytes()
|
|
||||||
}
|
|
||||||
HandleKind::Compose => "basic-latin\n".as_bytes().to_vec(),
|
HandleKind::Compose => "basic-latin\n".as_bytes().to_vec(),
|
||||||
HandleKind::Candidates => {
|
HandleKind::Candidates => {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
@@ -267,9 +303,34 @@ impl redox_scheme::SchemeBlockMut for ImeScheme {
|
|||||||
match &handle.kind {
|
match &handle.kind {
|
||||||
HandleKind::Compose => {
|
HandleKind::Compose => {
|
||||||
let input = String::from_utf8_lossy(buf);
|
let input = String::from_utf8_lossy(buf);
|
||||||
if input.trim() == "reset" {
|
for line in input.lines() {
|
||||||
self.engine.reset();
|
let line = line.trim();
|
||||||
self.state = InputState::default();
|
if line == "reset" {
|
||||||
|
self.engine.reset();
|
||||||
|
self.state = InputState::default();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut fields = line.split_whitespace();
|
||||||
|
let Some("feed") = fields.next() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(scancode) = fields.next().and_then(|field| field.parse::<u8>().ok())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(pressed) = fields.next().and_then(|field| field.parse::<bool>().ok())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let shift = fields
|
||||||
|
.next()
|
||||||
|
.and_then(|field| field.parse::<bool>().ok())
|
||||||
|
.unwrap_or(false);
|
||||||
|
let altgr = fields
|
||||||
|
.next()
|
||||||
|
.and_then(|field| field.parse::<bool>().ok())
|
||||||
|
.unwrap_or(false);
|
||||||
|
self.feed(scancode, pressed, shift, altgr);
|
||||||
}
|
}
|
||||||
Ok(Some(buf.len()))
|
Ok(Some(buf.len()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ impl Keymap {
|
|||||||
dead_keys: Vec<DeadKeyEntry>,
|
dead_keys: Vec<DeadKeyEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let file: KeymapFile = serde_json::from_str(json_str)
|
let file: KeymapFile =
|
||||||
.map_err(|e| format!("parse error: {}", e))?;
|
serde_json::from_str(json_str).map_err(|e| format!("parse error: {}", e))?;
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for entry in file.entries {
|
for entry in file.entries {
|
||||||
@@ -119,192 +119,609 @@ impl BuiltinKeymaps {
|
|||||||
|
|
||||||
fn common_dead_keys() -> Vec<DeadKeyEntry> {
|
fn common_dead_keys() -> Vec<DeadKeyEntry> {
|
||||||
vec![
|
vec![
|
||||||
DeadKeyEntry { dead_key: '`', base: 'a', composed: 'à' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '`', base: 'e', composed: 'è' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '`', base: 'i', composed: 'ì' },
|
base: 'a',
|
||||||
DeadKeyEntry { dead_key: '`', base: 'o', composed: 'ò' },
|
composed: 'à',
|
||||||
DeadKeyEntry { dead_key: '`', base: 'u', composed: 'ù' },
|
},
|
||||||
DeadKeyEntry { dead_key: '`', base: 'A', composed: 'À' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '`', base: 'E', composed: 'È' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '`', base: 'I', composed: 'Ì' },
|
base: 'e',
|
||||||
DeadKeyEntry { dead_key: '`', base: 'O', composed: 'Ò' },
|
composed: 'è',
|
||||||
DeadKeyEntry { dead_key: '`', base: 'U', composed: 'Ù' },
|
},
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'a', composed: 'á' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'e', composed: 'é' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'i', composed: 'í' },
|
base: 'i',
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'o', composed: 'ó' },
|
composed: 'ì',
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'u', composed: 'ú' },
|
},
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'A', composed: 'Á' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'E', composed: 'É' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'I', composed: 'Í' },
|
base: 'o',
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'O', composed: 'Ó' },
|
composed: 'ò',
|
||||||
DeadKeyEntry { dead_key: '\'', base: 'U', composed: 'Ú' },
|
},
|
||||||
DeadKeyEntry { dead_key: '^', base: 'a', composed: 'â' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '^', base: 'e', composed: 'ê' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '^', base: 'i', composed: 'î' },
|
base: 'u',
|
||||||
DeadKeyEntry { dead_key: '^', base: 'o', composed: 'ô' },
|
composed: 'ù',
|
||||||
DeadKeyEntry { dead_key: '^', base: 'u', composed: 'û' },
|
},
|
||||||
DeadKeyEntry { dead_key: '^', base: 'A', composed: 'Â' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '^', base: 'E', composed: 'Ê' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '^', base: 'I', composed: 'Î' },
|
base: 'A',
|
||||||
DeadKeyEntry { dead_key: '^', base: 'O', composed: 'Ô' },
|
composed: 'À',
|
||||||
DeadKeyEntry { dead_key: '^', base: 'U', composed: 'Û' },
|
},
|
||||||
DeadKeyEntry { dead_key: '"', base: 'a', composed: 'ä' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '"', base: 'e', composed: 'ë' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '"', base: 'i', composed: 'ï' },
|
base: 'E',
|
||||||
DeadKeyEntry { dead_key: '"', base: 'o', composed: 'ö' },
|
composed: 'È',
|
||||||
DeadKeyEntry { dead_key: '"', base: 'u', composed: 'ü' },
|
},
|
||||||
DeadKeyEntry { dead_key: '"', base: 'A', composed: 'Ä' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '"', base: 'E', composed: 'Ë' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '"', base: 'I', composed: 'Ï' },
|
base: 'I',
|
||||||
DeadKeyEntry { dead_key: '"', base: 'O', composed: 'Ö' },
|
composed: 'Ì',
|
||||||
DeadKeyEntry { dead_key: '"', base: 'U', composed: 'Ü' },
|
},
|
||||||
DeadKeyEntry { dead_key: '~', base: 'a', composed: 'ã' },
|
DeadKeyEntry {
|
||||||
DeadKeyEntry { dead_key: '~', base: 'n', composed: 'ñ' },
|
dead_key: '`',
|
||||||
DeadKeyEntry { dead_key: '~', base: 'o', composed: 'õ' },
|
base: 'O',
|
||||||
DeadKeyEntry { dead_key: '~', base: 'A', composed: 'Ã' },
|
composed: 'Ò',
|
||||||
DeadKeyEntry { dead_key: '~', base: 'N', 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 {
|
fn make_us() -> Keymap {
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
let bindings: &[(u8, char, char)] = &[
|
let bindings: &[(u8, char, char)] = &[
|
||||||
(0x01, '\x1B', '\x1B'), (0x02, '1', '!'), (0x03, '2', '@'),
|
(0x01, '\x1B', '\x1B'),
|
||||||
(0x04, '3', '#'), (0x05, '4', '$'), (0x06, '5', '%'),
|
(0x02, '1', '!'),
|
||||||
(0x07, '6', '^'), (0x08, '7', '&'), (0x09, '8', '*'),
|
(0x03, '2', '@'),
|
||||||
(0x0A, '9', '('), (0x0B, '0', ')'), (0x0C, '-', '_'),
|
(0x04, '3', '#'),
|
||||||
(0x0D, '=', '+'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
|
(0x05, '4', '$'),
|
||||||
(0x10, 'q', 'Q'), (0x11, 'w', 'W'), (0x12, 'e', 'E'),
|
(0x06, '5', '%'),
|
||||||
(0x13, 'r', 'R'), (0x14, 't', 'T'), (0x15, 'y', 'Y'),
|
(0x07, '6', '^'),
|
||||||
(0x16, 'u', 'U'), (0x17, 'i', 'I'), (0x18, 'o', 'O'),
|
(0x08, '7', '&'),
|
||||||
(0x19, 'p', 'P'), (0x1A, '[', '{'), (0x1B, ']', '}'),
|
(0x09, '8', '*'),
|
||||||
(0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 's', 'S'),
|
(0x0A, '9', '('),
|
||||||
(0x20, 'd', 'D'), (0x21, 'f', 'F'), (0x22, 'g', 'G'),
|
(0x0B, '0', ')'),
|
||||||
(0x23, 'h', 'H'), (0x24, 'j', 'J'), (0x25, 'k', 'K'),
|
(0x0C, '-', '_'),
|
||||||
(0x26, 'l', 'L'), (0x27, ';', ':'), (0x28, '\'', '"'),
|
(0x0D, '=', '+'),
|
||||||
(0x29, '`', '~'), (0x2B, '\\', '|'), (0x2C, 'z', 'Z'),
|
(0x0E, '\x7F', '\x7F'),
|
||||||
(0x2D, 'x', 'X'), (0x2E, 'c', 'C'), (0x2F, 'v', 'V'),
|
(0x0F, '\t', '\t'),
|
||||||
(0x30, 'b', 'B'), (0x31, 'n', 'N'), (0x32, 'm', 'M'),
|
(0x10, 'q', 'Q'),
|
||||||
(0x33, ',', '<'), (0x34, '.', '>'), (0x35, '/', '?'),
|
(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, ' ', ' '),
|
(0x39, ' ', ' '),
|
||||||
];
|
];
|
||||||
for &(sc, normal, shifted) in bindings {
|
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();
|
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 {
|
fn make_gb() -> Keymap {
|
||||||
let mut km = Self::make_us();
|
let mut km = Self::make_us();
|
||||||
km.name = "gb".into();
|
km.name = "gb".into();
|
||||||
if let Some(e) = km.entries.get_mut(&0x29) { e.shifted = '¬'; }
|
if let Some(e) = km.entries.get_mut(&0x29) {
|
||||||
if let Some(e) = km.entries.get_mut(&0x2B) { e.normal = '#'; e.shifted = '~'; }
|
e.shifted = '¬';
|
||||||
|
}
|
||||||
|
if let Some(e) = km.entries.get_mut(&0x2B) {
|
||||||
|
e.normal = '#';
|
||||||
|
e.shifted = '~';
|
||||||
|
}
|
||||||
km
|
km
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_dvorak() -> Keymap {
|
fn make_dvorak() -> Keymap {
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
let remap: &[(u8, char, char)] = &[
|
let remap: &[(u8, char, char)] = &[
|
||||||
(0x01, '\x1B', '\x1B'), (0x02, '1', '!'), (0x03, '2', '@'),
|
(0x01, '\x1B', '\x1B'),
|
||||||
(0x04, '3', '#'), (0x05, '4', '$'), (0x06, '5', '%'),
|
(0x02, '1', '!'),
|
||||||
(0x07, '6', '^'), (0x08, '7', '&'), (0x09, '8', '*'),
|
(0x03, '2', '@'),
|
||||||
(0x0A, '9', '('), (0x0B, '0', ')'), (0x0C, '[', '{'),
|
(0x04, '3', '#'),
|
||||||
(0x0D, ']', '}'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
|
(0x05, '4', '$'),
|
||||||
(0x10, '\'', '"'), (0x11, ',', '<'), (0x12, '.', '>'),
|
(0x06, '5', '%'),
|
||||||
(0x13, 'p', 'P'), (0x14, 'y', 'Y'), (0x15, 'f', 'F'),
|
(0x07, '6', '^'),
|
||||||
(0x16, 'g', 'G'), (0x17, 'c', 'C'), (0x18, 'r', 'R'),
|
(0x08, '7', '&'),
|
||||||
(0x19, 'l', 'L'), (0x1A, '/', '?'), (0x1B, '=', '+'),
|
(0x09, '8', '*'),
|
||||||
(0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 'o', 'O'),
|
(0x0A, '9', '('),
|
||||||
(0x20, 'e', 'E'), (0x21, 'u', 'U'), (0x22, 'i', 'I'),
|
(0x0B, '0', ')'),
|
||||||
(0x23, 'd', 'D'), (0x24, 'h', 'H'), (0x25, 't', 'T'),
|
(0x0C, '[', '{'),
|
||||||
(0x26, 'n', 'N'), (0x27, 's', 'S'), (0x28, '-', '_'),
|
(0x0D, ']', '}'),
|
||||||
(0x29, '`', '~'), (0x2B, '\\', '|'), (0x2C, ';', ':'),
|
(0x0E, '\x7F', '\x7F'),
|
||||||
(0x2D, 'q', 'Q'), (0x2E, 'j', 'J'), (0x2F, 'k', 'K'),
|
(0x0F, '\t', '\t'),
|
||||||
(0x30, 'x', 'X'), (0x31, 'b', 'B'), (0x32, 'm', 'M'),
|
(0x10, '\'', '"'),
|
||||||
(0x33, 'w', 'W'), (0x34, 'v', 'V'), (0x35, 'z', 'Z'),
|
(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, ' ', ' '),
|
(0x39, ' ', ' '),
|
||||||
];
|
];
|
||||||
for &(sc, normal, shifted) in remap {
|
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 {
|
fn make_azerty() -> Keymap {
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
let bindings: &[(u8, char, char)] = &[
|
let bindings: &[(u8, char, char)] = &[
|
||||||
(0x01, '\x1B', '\x1B'), (0x02, '&', '1'), (0x03, 'é', '2'),
|
(0x01, '\x1B', '\x1B'),
|
||||||
(0x04, '"', '3'), (0x05, '\'', '4'), (0x06, '(', '5'),
|
(0x02, '&', '1'),
|
||||||
(0x07, '-', '6'), (0x08, 'è', '7'), (0x09, '_', '8'),
|
(0x03, 'é', '2'),
|
||||||
(0x0A, 'ç', '9'), (0x0B, 'à', '0'), (0x0C, ')', '°'),
|
(0x04, '"', '3'),
|
||||||
(0x0D, '=', '+'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
|
(0x05, '\'', '4'),
|
||||||
(0x10, 'a', 'A'), (0x11, 'z', 'Z'), (0x12, 'e', 'E'),
|
(0x06, '(', '5'),
|
||||||
(0x13, 'r', 'R'), (0x14, 't', 'T'), (0x15, 'y', 'Y'),
|
(0x07, '-', '6'),
|
||||||
(0x16, 'u', 'U'), (0x17, 'i', 'I'), (0x18, 'o', 'O'),
|
(0x08, 'è', '7'),
|
||||||
(0x19, 'p', 'P'), (0x1A, '^', '¨'), (0x1B, '$', '£'),
|
(0x09, '_', '8'),
|
||||||
(0x1C, '\n', '\n'), (0x1E, 'q', 'Q'), (0x1F, 's', 'S'),
|
(0x0A, 'ç', '9'),
|
||||||
(0x20, 'd', 'D'), (0x21, 'f', 'F'), (0x22, 'g', 'G'),
|
(0x0B, 'à', '0'),
|
||||||
(0x23, 'h', 'H'), (0x24, 'j', 'J'), (0x25, 'k', 'K'),
|
(0x0C, ')', '°'),
|
||||||
(0x26, 'l', 'L'), (0x27, 'm', 'M'), (0x28, 'ù', '%'),
|
(0x0D, '=', '+'),
|
||||||
(0x29, '²', '~'), (0x2B, '*', 'µ'), (0x2C, 'w', 'W'),
|
(0x0E, '\x7F', '\x7F'),
|
||||||
(0x2D, 'x', 'X'), (0x2E, 'c', 'C'), (0x2F, 'v', 'V'),
|
(0x0F, '\t', '\t'),
|
||||||
(0x30, 'b', 'B'), (0x31, 'n', 'N'), (0x32, ',', '?'),
|
(0x10, 'a', 'A'),
|
||||||
(0x33, ';', '.'), (0x34, ':', '/'), (0x35, '!', '§'),
|
(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, ' ', ' '),
|
(0x39, ' ', ' '),
|
||||||
];
|
];
|
||||||
for &(sc, normal, shifted) in bindings {
|
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();
|
let mut dead_keys = Self::common_dead_keys();
|
||||||
dead_keys.push(DeadKeyEntry { dead_key: '^', base: ' ', composed: '^' });
|
dead_keys.push(DeadKeyEntry {
|
||||||
dead_keys.push(DeadKeyEntry { dead_key: '¨', base: ' ', composed: '¨' });
|
dead_key: '^',
|
||||||
Keymap { name: "azerty".into(), entries, compose: Vec::new(), dead_keys }
|
base: ' ',
|
||||||
|
composed: '^',
|
||||||
|
});
|
||||||
|
dead_keys.push(DeadKeyEntry {
|
||||||
|
dead_key: '¨',
|
||||||
|
base: ' ',
|
||||||
|
composed: '¨',
|
||||||
|
});
|
||||||
|
Keymap {
|
||||||
|
name: "azerty".into(),
|
||||||
|
entries,
|
||||||
|
compose: Vec::new(),
|
||||||
|
dead_keys,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_bepo() -> Keymap {
|
fn make_bepo() -> Keymap {
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
let bindings: &[(u8, char, char)] = &[
|
let bindings: &[(u8, char, char)] = &[
|
||||||
(0x01, '\x1B', '\x1B'), (0x02, '"', '1'), (0x03, '«', '2'),
|
(0x01, '\x1B', '\x1B'),
|
||||||
(0x04, '»', '3'), (0x05, '(', '4'), (0x06, ')', '5'),
|
(0x02, '"', '1'),
|
||||||
(0x07, '@', '6'), (0x08, '+', '7'), (0x09, '-', '8'),
|
(0x03, '«', '2'),
|
||||||
(0x0A, '/', '9'), (0x0B, '*', '0'), (0x0C, '=', '°'),
|
(0x04, '»', '3'),
|
||||||
(0x0D, '%', '`'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'),
|
(0x05, '(', '4'),
|
||||||
(0x10, 'b', 'B'), (0x11, 'é', 'É'), (0x12, 'p', 'P'),
|
(0x06, ')', '5'),
|
||||||
(0x13, 'o', 'O'), (0x14, 'è', 'È'), (0x15, '^', '!'),
|
(0x07, '@', '6'),
|
||||||
(0x16, 'v', 'V'), (0x17, 'd', 'D'), (0x18, 'l', 'L'),
|
(0x08, '+', '7'),
|
||||||
(0x19, 'j', 'J'), (0x1A, 'z', 'Z'), (0x1B, 'w', 'W'),
|
(0x09, '-', '8'),
|
||||||
(0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 'u', 'U'),
|
(0x0A, '/', '9'),
|
||||||
(0x20, 'i', 'I'), (0x21, 'e', 'E'), (0x22, ',', ';'),
|
(0x0B, '*', '0'),
|
||||||
(0x23, 'c', 'C'), (0x24, 't', 'T'), (0x25, 's', 'S'),
|
(0x0C, '=', '°'),
|
||||||
(0x26, 'r', 'R'), (0x27, 'n', 'N'), (0x28, 'm', 'M'),
|
(0x0D, '%', '`'),
|
||||||
(0x29, 'ç', 'Ç'), (0x2B, '\'', '?'), (0x2C, 'x', 'X'),
|
(0x0E, '\x7F', '\x7F'),
|
||||||
(0x2D, 'f', 'F'), (0x2E, 'h', 'H'), (0x2F, 'q', 'Q'),
|
(0x0F, '\t', '\t'),
|
||||||
(0x30, 'g', 'G'), (0x31, 'y', 'Y'), (0x32, 'k', 'K'),
|
(0x10, 'b', 'B'),
|
||||||
(0x33, '.', ':'), (0x34, '/', '§'), (0x35, 'g', 'G'),
|
(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, ' ', ' '),
|
(0x39, ' ', ' '),
|
||||||
];
|
];
|
||||||
for &(sc, normal, shifted) in bindings {
|
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();
|
let mut dead_keys = Self::common_dead_keys();
|
||||||
dead_keys.push(DeadKeyEntry { dead_key: '^', base: ' ', composed: '^' });
|
dead_keys.push(DeadKeyEntry {
|
||||||
Keymap { name: "bepo".into(), entries, compose: Vec::new(), dead_keys }
|
dead_key: '^',
|
||||||
|
base: ' ',
|
||||||
|
composed: '^',
|
||||||
|
});
|
||||||
|
Keymap {
|
||||||
|
name: "bepo".into(),
|
||||||
|
entries,
|
||||||
|
compose: Vec::new(),
|
||||||
|
dead_keys,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_it() -> Keymap {
|
fn make_it() -> Keymap {
|
||||||
let mut km = Self::make_us();
|
let mut km = Self::make_us();
|
||||||
km.name = "it".into();
|
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(&0x29) {
|
||||||
if let Some(e) = km.entries.get_mut(&0x0C) { e.normal = '\''; e.shifted = '?'; }
|
e.normal = '\\';
|
||||||
if let Some(e) = km.entries.get_mut(&0x0D) { e.normal = 'ì'; e.shifted = '^'; }
|
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(&0x0C) {
|
||||||
if let Some(e) = km.entries.get_mut(&0x27) { e.normal = 'ò'; e.shifted = 'ç'; }
|
e.normal = '\'';
|
||||||
if let Some(e) = km.entries.get_mut(&0x28) { e.normal = 'à'; e.shifted = '°'; }
|
e.shifted = '?';
|
||||||
if let Some(e) = km.entries.get_mut(&0x2B) { 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
|
km
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ mod xkb;
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use scheme::KeymapScheme;
|
use scheme::KeymapScheme;
|
||||||
|
|
||||||
@@ -24,13 +22,33 @@ fn main() {
|
|||||||
Err(_) => "/etc/keymaps".to_string(),
|
Err(_) => "/etc/keymaps".to_string(),
|
||||||
};
|
};
|
||||||
if let Err(e) = scheme.load_from_dir(&keymap_dir) {
|
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")
|
log_msg(
|
||||||
.expect("keymapd: failed to register scheme:keymap");
|
"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");
|
log_msg("INFO", "registered scheme:keymap");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -41,15 +59,16 @@ fn main() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log_msg("WARN", &format!("scheme read error (ignoring): {}", e));
|
log_msg("ERROR", &format!("scheme read error: {}", e));
|
||||||
thread::sleep(Duration::from_millis(100));
|
break;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match request.handle_scheme_block_mut(&mut scheme) {
|
match request.handle_scheme_block_mut(&mut scheme) {
|
||||||
Ok(response) => {
|
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));
|
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;
|
use crate::keymap::Keymap;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
enum HandleKind {
|
enum HandleKind {
|
||||||
Root,
|
Root,
|
||||||
Active,
|
Active,
|
||||||
@@ -69,7 +70,12 @@ impl KeymapScheme {
|
|||||||
Ok(())
|
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)
|
let km = crate::xkb::load_xkb_keymap(xkb_dir, layout, variant)
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
let name = match variant {
|
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>> {
|
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 => {
|
HandleKind::Root => {
|
||||||
let mut listing = String::new();
|
let mut listing = String::new();
|
||||||
listing.push_str("active\nlist\n");
|
listing.push_str("active\nlist\n");
|
||||||
@@ -147,7 +156,12 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme {
|
|||||||
}
|
}
|
||||||
listing.into_bytes()
|
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 => {
|
HandleKind::List => {
|
||||||
let mut listing = String::new();
|
let mut listing = String::new();
|
||||||
for (i, name) in self.keymaps.keys().enumerate() {
|
for (i, name) in self.keymaps.keys().enumerate() {
|
||||||
@@ -162,20 +176,25 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme {
|
|||||||
HandleKind::Keymap { name } => {
|
HandleKind::Keymap { name } => {
|
||||||
let km = self.keymaps.get(name).ok_or(Error::new(ENOENT))?;
|
let km = self.keymaps.get(name).ok_or(Error::new(ENOENT))?;
|
||||||
format!(
|
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.name,
|
||||||
km.entries.len(),
|
km.entries.len(),
|
||||||
km.compose.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()
|
.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));
|
return Ok(Some(0));
|
||||||
}
|
}
|
||||||
let remaining = &content[handle.offset..];
|
let remaining = &content[offset..];
|
||||||
let to_copy = remaining.len().min(buf.len());
|
let to_copy = remaining.len().min(buf.len());
|
||||||
buf[..to_copy].copy_from_slice(&remaining[..to_copy]);
|
buf[..to_copy].copy_from_slice(&remaining[..to_copy]);
|
||||||
handle.offset += to_copy;
|
handle.offset += to_copy;
|
||||||
|
|||||||
@@ -210,10 +210,22 @@ struct XkbKeyEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_keysyms(syms: &[&str]) -> (char, char, char, 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 normal = syms
|
||||||
let shifted = syms.get(1).map(|s| keysym_to_char(s.trim())).unwrap_or('\0');
|
.get(0)
|
||||||
let altgr = syms.get(2).map(|s| keysym_to_char(s.trim())).unwrap_or('\0');
|
.map(|s| keysym_to_char(s.trim()))
|
||||||
let altgr_shifted = syms.get(3).map(|s| keysym_to_char(s.trim())).unwrap_or('\0');
|
.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)
|
(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 inner.starts_with("key <") {
|
||||||
if let Some(entry) = parse_key_line(inner) {
|
if let Some(entry) = parse_key_line(inner) {
|
||||||
entries.entry(entry.scancode).or_insert_with(|| {
|
entries
|
||||||
KeymapEntry {
|
.entry(entry.scancode)
|
||||||
|
.or_insert_with(|| KeymapEntry {
|
||||||
scancode: entry.scancode,
|
scancode: entry.scancode,
|
||||||
normal: entry.normal,
|
normal: entry.normal,
|
||||||
shifted: entry.shifted,
|
shifted: entry.shifted,
|
||||||
altgr: entry.altgr,
|
altgr: entry.altgr,
|
||||||
altgr_shifted: entry.altgr_shifted,
|
altgr_shifted: entry.altgr_shifted,
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -260,7 +272,10 @@ pub fn parse_xkb_symbols(content: &str, variant: Option<&str>) -> Result<Keymap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found_variant {
|
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 {
|
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 file_path = format!("{}/symbols/{}", xkb_dir, layout);
|
||||||
let content = std::fs::read_to_string(&file_path)
|
let content = std::fs::read_to_string(&file_path)
|
||||||
.map_err(|e| format!("failed to read XKB symbols file '{}': {}", file_path, e))?;
|
.map_err(|e| format!("failed to read XKB symbols file '{}': {}", file_path, e))?;
|
||||||
|
|||||||
@@ -7,18 +7,38 @@ use std::os::unix::process::CommandExt;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use libredox::Fd;
|
use libredox::Fd;
|
||||||
use redox_scheme::Socket;
|
|
||||||
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
||||||
|
use redox_scheme::Socket;
|
||||||
|
|
||||||
unsafe fn get_fd(var: &str) -> RawFd {
|
unsafe fn get_fd(var: &str) -> Option<RawFd> {
|
||||||
let fd: RawFd = std::env::var(var).unwrap().parse().unwrap();
|
let value = match std::env::var(var) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => {
|
||||||
|
let exe = std::env::args()
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| "daemon".to_string());
|
||||||
|
eprintln!("daemon: {var} not set for {exe}; readiness notification disabled");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let fd: RawFd = match value.parse() {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(err) => {
|
||||||
|
let exe = std::env::args()
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| "daemon".to_string());
|
||||||
|
eprintln!("daemon: invalid {var} value {value:?} for {exe}: {err}; readiness notification disabled");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
|
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
|
||||||
panic!(
|
eprintln!(
|
||||||
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
||||||
io::Error::last_os_error()
|
io::Error::last_os_error()
|
||||||
);
|
);
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
fd
|
Some(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
||||||
@@ -38,20 +58,22 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
|||||||
/// A long running background process that handles requests.
|
/// A long running background process that handles requests.
|
||||||
#[must_use = "Daemon::ready must be called"]
|
#[must_use = "Daemon::ready must be called"]
|
||||||
pub struct Daemon {
|
pub struct Daemon {
|
||||||
write_pipe: PipeWriter,
|
write_pipe: Option<PipeWriter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Daemon {
|
impl Daemon {
|
||||||
/// Create a new daemon.
|
/// Create a new daemon.
|
||||||
pub fn new(f: impl FnOnce(Daemon) -> !) -> ! {
|
pub fn new(f: impl FnOnce(Daemon) -> !) -> ! {
|
||||||
let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
|
let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
|
||||||
|
|
||||||
f(Daemon { write_pipe })
|
f(Daemon { write_pipe })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notify the process that the daemon is ready to accept requests.
|
/// Notify the process that the daemon is ready to accept requests.
|
||||||
pub fn ready(mut self) {
|
pub fn ready(mut self) {
|
||||||
self.write_pipe.write_all(&[0]).unwrap();
|
if let Some(write_pipe) = self.write_pipe.as_mut() {
|
||||||
|
write_pipe.write_all(&[0]).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `Command` as a child process.
|
/// Executes `Command` as a child process.
|
||||||
@@ -83,25 +105,27 @@ impl Daemon {
|
|||||||
/// A long running background process that handles requests using schemes.
|
/// A long running background process that handles requests using schemes.
|
||||||
#[must_use = "SchemeDaemon::ready must be called"]
|
#[must_use = "SchemeDaemon::ready must be called"]
|
||||||
pub struct SchemeDaemon {
|
pub struct SchemeDaemon {
|
||||||
write_pipe: PipeWriter,
|
write_pipe: Option<PipeWriter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchemeDaemon {
|
impl SchemeDaemon {
|
||||||
/// Create a new daemon for use with schemes.
|
/// Create a new daemon for use with schemes.
|
||||||
pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! {
|
pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! {
|
||||||
let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
|
let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
|
||||||
|
|
||||||
f(SchemeDaemon { write_pipe })
|
f(SchemeDaemon { write_pipe })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notify the process that the scheme daemon is ready to accept requests.
|
/// Notify the process that the scheme daemon is ready to accept requests.
|
||||||
pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> {
|
pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> {
|
||||||
syscall::call_wo(
|
if let Some(write_pipe) = self.write_pipe {
|
||||||
self.write_pipe.as_raw_fd() as usize,
|
syscall::call_wo(
|
||||||
&cap_fd.into_raw().to_ne_bytes(),
|
write_pipe.as_raw_fd() as usize,
|
||||||
syscall::CallFlags::FD,
|
&cap_fd.into_raw().to_ne_bytes(),
|
||||||
&[],
|
syscall::CallFlags::FD,
|
||||||
)?;
|
&[],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ git = "https://gitlab.redox-os.org/redox-os/base.git"
|
|||||||
rev = "463f76b9608a896e6f6c9f63457f57f6409873c7"
|
rev = "463f76b9608a896e6f6c9f63457f57f6409873c7"
|
||||||
patches = [
|
patches = [
|
||||||
"P0-daemon-fix-init-notify-unwrap.patch",
|
"P0-daemon-fix-init-notify-unwrap.patch",
|
||||||
"P0-daemon-silence-init-notify.patch",
|
|
||||||
"P0-workspace-add-bootstrap.patch",
|
"P0-workspace-add-bootstrap.patch",
|
||||||
"P0-init-continuous-scheduling.patch",
|
"P0-init-continuous-scheduling.patch",
|
||||||
# TODO: P6-e1000d-msi-migration.patch conflicts with P6-driver-main-fixes
|
# TODO: P6-e1000d-msi-migration.patch conflicts with P6-driver-main-fixes
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ patches = [
|
|||||||
"P3-inet6-pton-ntop.patch",
|
"P3-inet6-pton-ntop.patch",
|
||||||
"P3-tcp-nodelay.patch",
|
"P3-tcp-nodelay.patch",
|
||||||
"P3-tcp-sockopt-forward.patch",
|
"P3-tcp-sockopt-forward.patch",
|
||||||
# Named POSIX semaphores (sem_open/close/unlink) — comprehensive implementation
|
# Named POSIX semaphores (sem_open/close/unlink) — comprehensive + refcount + va_list
|
||||||
"P3-semaphore-comprehensive.patch",
|
"P3-semaphore-comprehensive.patch",
|
||||||
# Reverse From<&syscall::TimeSpec> for timespec (needed by getrusage)
|
# Reverse From<&syscall::TimeSpec> for timespec (needed by getrusage)
|
||||||
"P3-timespec-reverse-from.patch",
|
"P3-timespec-reverse-from.patch",
|
||||||
|
|||||||
Reference in New Issue
Block a user