Fix compositor output routing and pointer hints; format imports

This commit is contained in:
Samuel Onoja committed 2026-09-30 06:39:58 +01:00
1 parent 88e4f3c135
commit 46509f880a
108 files changed
+3682 -8371

No files matched your search

+47 -101
View File
@@ -1,23 +1,15 @@
use std::{
fs::File,
io::Write,
os::{
fd::{AsFd, OwnedFd},
unix::net::UnixStream,
},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
use std::fs::File;
use std::io::Write;
use std::os::fd::{AsFd, OwnedFd};
use std::os::unix::net::UnixStream;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use calloop::{EventLoop, LoopSignal, PostAction, channel};
use reis::{
calloop::{EisRequestSource, EisRequestSourceEvent},
eis,
enumflags2::BitFlags,
request::{Connection, Device, DeviceCapability, EisRequest},
};
use reis::calloop::{EisRequestSource, EisRequestSourceEvent};
use reis::eis;
use reis::enumflags2::BitFlags;
use reis::request::{Connection, Device, DeviceCapability, EisRequest};
use serde_json::Value;
use crate::backend::Cancel;
@@ -29,11 +21,7 @@ pub(crate) struct Worker {
}
impl Worker {
pub(crate) fn start(
capabilities: u32,
keymap: &str,
cancel: Arc<Cancel>,
) -> Result<(Self, OwnedFd), String> {
pub(crate) fn start(capabilities: u32, keymap: &str, cancel: Arc<Cancel>) -> Result<(Self, OwnedFd), String> {
if keymap.is_empty() || keymap.len() > 256 * 1024 {
return Err("Invalid captured keyboard map".into());
}
@@ -59,8 +47,7 @@ impl Worker {
let _finish = Finish(cancel);
let run = || {
let context = eis::Context::new(server).map_err(|e| e.to_string())?;
let mut event_loop =
EventLoop::<State>::try_new().map_err(|e| e.to_string())?;
let mut event_loop = EventLoop::<State>::try_new().map_err(|e| e.to_string())?;
let signal = event_loop.get_signal();
let (sender, receiver) = channel::sync_channel(4);
event_loop
@@ -72,22 +59,19 @@ impl Worker {
.map_err(|e| e.to_string())?;
event_loop
.handle()
.insert_source(
EisRequestSource::new(context, 1),
|event, connection, state| {
let action = match event {
Ok(event) => state.request(connection, event),
Err(_) => {
state.signal.stop();
PostAction::Remove
}
};
if connection.flush().is_err() {
.insert_source(EisRequestSource::new(context, 1), |event, connection, state| {
let action = match event {
Ok(event) => state.request(connection, event),
Err(_) => {
state.signal.stop();
PostAction::Remove
}
Ok(action)
},
)
};
if connection.flush().is_err() {
state.signal.stop();
}
Ok(action)
})
.map_err(|e| e.to_string())?;
let mut state = State {
signal: signal.clone(),
@@ -107,8 +91,7 @@ impl Worker {
.map_err(|_| "Input transport startup cancelled".to_owned())?;
let _ = event_loop.run(None, &mut state, |_| {});
if let Some(connection) = state.connection.take() {
connection
.disconnected(eis::connection::DisconnectReason::Disconnected, None);
connection.disconnected(eis::connection::DisconnectReason::Disconnected, None);
}
state.ready.store(false, Ordering::SeqCst);
Ok::<(), String>(())
@@ -121,14 +104,7 @@ impl Worker {
let (sender, signal) = started
.recv_timeout(std::time::Duration::from_secs(2))
.map_err(|_| "Input transport startup timed out".to_owned())??;
Ok((
Self {
sender,
signal,
ready,
},
client.into(),
))
Ok((Self { sender, signal, ready }, client.into()))
}
pub(crate) fn send(&self, events: Vec<Value>) -> Result<(), String> {
@@ -180,18 +156,14 @@ impl State {
capabilities |= DeviceCapability::Keyboard;
}
if self.capabilities & 2 != 0 {
capabilities |= DeviceCapability::Pointer
| DeviceCapability::Button
| DeviceCapability::Scroll;
capabilities |= DeviceCapability::Pointer | DeviceCapability::Button | DeviceCapability::Scroll;
}
let _ = connection.add_seat(Some("Ferese input capture"), capabilities);
self.connection = Some(connection.clone());
}
EisRequestSourceEvent::Request(EisRequest::Bind(request)) => {
let keyboard = request.capabilities.contains(DeviceCapability::Keyboard)
&& self.capabilities & 1 != 0;
let pointer = request.capabilities.contains(DeviceCapability::Pointer)
&& self.capabilities & 2 != 0;
let keyboard = request.capabilities.contains(DeviceCapability::Keyboard) && self.capabilities & 1 != 0;
let pointer = request.capabilities.contains(DeviceCapability::Pointer) && self.capabilities & 2 != 0;
if !keyboard && let Some(device) = self.keyboard.take() {
device.remove();
self.keyboard_ready = false;
@@ -207,11 +179,7 @@ impl State {
DeviceCapability::Keyboard.into(),
|device| {
if let Some(keyboard) = device.interface::<eis::Keyboard>() {
keyboard.keymap(
eis::keyboard::KeymapType::Xkb,
self.keymap_size,
self.keymap.as_fd(),
);
keyboard.keymap(eis::keyboard::KeymapType::Xkb, self.keymap_size, self.keymap.as_fd());
}
},
);
@@ -221,15 +189,11 @@ impl State {
}
if pointer && self.pointer.is_none() {
let caps = request.capabilities
& (DeviceCapability::Pointer
| DeviceCapability::Button
| DeviceCapability::Scroll);
let device = request.seat.add_device(
Some("Ferese pointer"),
eis::device::DeviceType::Virtual,
caps,
|_| {},
);
& (DeviceCapability::Pointer | DeviceCapability::Button | DeviceCapability::Scroll);
let device =
request
.seat
.add_device(Some("Ferese pointer"), eis::device::DeviceType::Virtual, caps, |_| {});
self.pointer_ready = device.device().version() < 3;
device.resumed();
self.pointer = Some(device);
@@ -263,14 +227,11 @@ impl State {
return PostAction::Remove;
}
self.ready.store(
(self.keyboard.is_none() || self.keyboard_ready)
&& (self.pointer.is_none() || self.pointer_ready),
(self.keyboard.is_none() || self.keyboard_ready) && (self.pointer.is_none() || self.pointer_ready),
Ordering::SeqCst,
);
}
EisRequestSourceEvent::Request(
EisRequest::Disconnect | EisRequest::DeviceClosed(_),
) => {
EisRequestSourceEvent::Request(EisRequest::Disconnect | EisRequest::DeviceClosed(_)) => {
self.signal.stop();
return PostAction::Remove;
}
@@ -307,12 +268,7 @@ impl State {
if let Some(device) = &self.keyboard
&& let Some(keyboard) = device.interface::<eis::Keyboard>()
{
for key in event["keys"]
.as_array()
.into_iter()
.flatten()
.filter_map(Value::as_u64)
{
for key in event["keys"].as_array().into_iter().flatten().filter_map(Value::as_u64) {
keyboard.key(key as u32, eis::keyboard::KeyState::Press);
}
device.frame(now());
@@ -392,10 +348,7 @@ impl State {
}
fn now() -> u64 {
let mut time = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
let mut time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
// SAFETY: the initialized output pointer covers a timespec allocation.
if unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time) } == 0 {
time.tv_sec as u64 * 1_000_000 + time.tv_nsec as u64 / 1_000
@@ -406,12 +359,12 @@ fn now() -> u64 {
#[cfg(test)]
mod tests {
use super::*;
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
use serde_json::json;
use std::{
process::{Command, Stdio},
time::{Duration, Instant},
};
use super::*;
#[test]
#[ignore = "requires Python and the system libei receiver library"]
@@ -419,8 +372,8 @@ mod tests {
let cancel = Arc::new(Cancel::default());
let map = "xkb_keymap { xkb_keycodes { include \"evdev+aliases(qwerty)\" }; xkb_types { include \"complete\" }; xkb_compatibility { include \"complete\" }; xkb_symbols { include \"pc+us+inet(evdev)\" }; };";
let (worker, fd) = Worker::start(3, map, cancel.clone()).unwrap();
let fixture = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../scripts/tests/fixtures/eis-receiver.py");
let fixture =
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../scripts/tests/fixtures/eis-receiver.py");
let mut client = Command::new("python3")
.arg(fixture)
.stdin(Stdio::from(fd))
@@ -435,10 +388,7 @@ mod tests {
if !worker.ready.load(Ordering::SeqCst) {
let _ = client.kill();
let output = client.wait_with_output().unwrap();
panic!(
"Receiver did not bind: {}",
String::from_utf8_lossy(&output.stderr)
);
panic!("Receiver did not bind: {}", String::from_utf8_lossy(&output.stderr));
}
worker
.send(vec![
@@ -453,11 +403,7 @@ mod tests {
])
.unwrap();
let output = client.wait_with_output().unwrap();
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
assert!(String::from_utf8_lossy(&output.stdout).contains("\"keymap\": true"));
let deadline = Instant::now() + Duration::from_secs(2);
while !cancel.stopped.load(Ordering::SeqCst) && Instant::now() < deadline {