refactor: split cargo workspace
This commit is contained in:
parent
5db14936e7
commit
1c21bd1259
695 changed files with 32023 additions and 44964 deletions
|
|
@ -1,8 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::ClientCaps,
|
||||
security_context_acceptor::AcceptorMetadata,
|
||||
client::ClientMetadata,
|
||||
state::State,
|
||||
utils::{
|
||||
errorfmt::ErrorFmt,
|
||||
|
|
@ -46,63 +45,49 @@ struct AllocatedSocket {
|
|||
name: String,
|
||||
// /run/user/1000/wayland-x
|
||||
path: Ustring,
|
||||
insecure: Rc<OwnedFd>,
|
||||
socket: Rc<OwnedFd>,
|
||||
// /run/user/1000/wayland-x.lock
|
||||
lock_path: Ustring,
|
||||
_lock_fd: OwnedFd,
|
||||
// /run/user/1000/wayland-x.jay
|
||||
secure_path: Ustring,
|
||||
secure: Rc<OwnedFd>,
|
||||
}
|
||||
|
||||
impl Drop for AllocatedSocket {
|
||||
fn drop(&mut self) {
|
||||
let _ = uapi::unlink(&self.path);
|
||||
let _ = uapi::unlink(&self.lock_path);
|
||||
let _ = uapi::unlink(&self.secure_path);
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_socket(
|
||||
insecure: &Rc<OwnedFd>,
|
||||
secure: &Rc<OwnedFd>,
|
||||
xrd: &str,
|
||||
id: u32,
|
||||
) -> Result<AllocatedSocket, AcceptorError> {
|
||||
fn bind_socket(socket: &Rc<OwnedFd>, xrd: &str, id: u32) -> Result<AllocatedSocket, AcceptorError> {
|
||||
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
||||
addr.sun_family = c::AF_UNIX as _;
|
||||
let name = format!("wayland-{}", id);
|
||||
let path = format_ustr!("{}/{}", xrd, name);
|
||||
let jay_path = format_ustr!("{}.jay", path.display());
|
||||
let lock_path = format_ustr!("{}.lock", path.display());
|
||||
if jay_path.len() + 1 > addr.sun_path.len() {
|
||||
if path.len() + 1 > addr.sun_path.len() {
|
||||
return Err(AcceptorError::XrdTooLong(xrd.to_string()));
|
||||
}
|
||||
let lock_fd = uapi::open(&*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_RDWR, 0o644)
|
||||
.map_os_err(AcceptorError::OpenLockFile)?;
|
||||
uapi::flock(lock_fd.raw(), c::LOCK_EX | c::LOCK_NB).map_os_err(AcceptorError::LockLockFile)?;
|
||||
for (name, fd) in [(&path, insecure), (&jay_path, secure)] {
|
||||
match uapi::lstat(name).to_os_error() {
|
||||
Ok(_) => {
|
||||
log::info!("Unlinking {}", name.display());
|
||||
let _ = uapi::unlink(name);
|
||||
}
|
||||
Err(OsError(c::ENOENT)) => {}
|
||||
Err(e) => return Err(AcceptorError::SocketStat(e)),
|
||||
match uapi::lstat(&path).to_os_error() {
|
||||
Ok(_) => {
|
||||
log::info!("Unlinking {}", path.display());
|
||||
let _ = uapi::unlink(&path);
|
||||
}
|
||||
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||
sun_path[..name.len()].copy_from_slice(name.as_bytes());
|
||||
sun_path[name.len()] = 0;
|
||||
uapi::bind(fd.raw(), &addr).map_os_err(AcceptorError::BindFailed)?;
|
||||
Err(OsError(c::ENOENT)) => {}
|
||||
Err(e) => return Err(AcceptorError::SocketStat(e)),
|
||||
}
|
||||
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||
sun_path[..path.len()].copy_from_slice(path.as_bytes());
|
||||
sun_path[path.len()] = 0;
|
||||
uapi::bind(socket.raw(), &addr).map_os_err(AcceptorError::BindFailed)?;
|
||||
Ok(AllocatedSocket {
|
||||
name,
|
||||
path,
|
||||
insecure: insecure.clone(),
|
||||
socket: socket.clone(),
|
||||
lock_path,
|
||||
_lock_fd: lock_fd,
|
||||
secure_path: jay_path,
|
||||
secure: secure.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -111,17 +96,11 @@ fn allocate_socket() -> Result<AllocatedSocket, AcceptorError> {
|
|||
Some(d) => d,
|
||||
_ => return Err(AcceptorError::XrdNotSet),
|
||||
};
|
||||
let mut fds = [None, None];
|
||||
for fd in &mut fds {
|
||||
let socket = uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
|
||||
.map(Rc::new)
|
||||
.map_os_err(AcceptorError::SocketFailed)?;
|
||||
*fd = Some(socket);
|
||||
}
|
||||
let unsecure = fds[0].take().unwrap();
|
||||
let secure = fds[1].take().unwrap();
|
||||
let socket = uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
|
||||
.map(Rc::new)
|
||||
.map_os_err(AcceptorError::SocketFailed)?;
|
||||
for i in 1..1000 {
|
||||
match bind_socket(&unsecure, &secure, &xrd, i) {
|
||||
match bind_socket(&socket, &xrd, i) {
|
||||
Ok(s) => return Ok(s),
|
||||
Err(e) => {
|
||||
log::warn!("Cannot use the wayland-{} socket: {}", i, ErrorFmt(e));
|
||||
|
|
@ -137,19 +116,12 @@ impl Acceptor {
|
|||
) -> Result<(Rc<Acceptor>, Vec<SpawnedFuture<()>>), AcceptorError> {
|
||||
let socket = allocate_socket()?;
|
||||
log::info!("bound to socket {}", socket.path.display());
|
||||
for fd in [&socket.secure, &socket.insecure] {
|
||||
uapi::listen(fd.raw(), 4096).map_os_err(AcceptorError::ListenFailed)?;
|
||||
}
|
||||
uapi::listen(socket.socket.raw(), 4096).map_os_err(AcceptorError::ListenFailed)?;
|
||||
let acc = Rc::new(Acceptor { socket });
|
||||
let futures = vec![
|
||||
state.eng.spawn(
|
||||
"secure acceptor",
|
||||
accept(acc.socket.secure.clone(), state.clone(), true),
|
||||
),
|
||||
state.eng.spawn(
|
||||
"insecure acceptor",
|
||||
accept(acc.socket.insecure.clone(), state.clone(), false),
|
||||
),
|
||||
state
|
||||
.eng
|
||||
.spawn("client acceptor", accept(acc.socket.socket.clone(), state.clone())),
|
||||
];
|
||||
state.acceptor.set(Some(acc.clone()));
|
||||
Ok((acc, futures))
|
||||
|
|
@ -160,16 +132,13 @@ impl Acceptor {
|
|||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
||||
pub fn secure_path(&self) -> &Ustr {
|
||||
self.socket.secure_path.as_ustr()
|
||||
pub fn socket_path(&self) -> &Ustr {
|
||||
self.socket.path.as_ustr()
|
||||
}
|
||||
}
|
||||
|
||||
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
|
||||
let metadata = Rc::new(AcceptorMetadata {
|
||||
secure,
|
||||
..Default::default()
|
||||
});
|
||||
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>) {
|
||||
let metadata = Rc::new(ClientMetadata::default());
|
||||
loop {
|
||||
let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await {
|
||||
Ok(fd) => fd,
|
||||
|
|
@ -181,7 +150,7 @@ async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
|
|||
let id = state.clients.id();
|
||||
if let Err(e) = state
|
||||
.clients
|
||||
.spawn(id, &state, fd, ClientCaps::all(), false, &metadata)
|
||||
.spawn(id, &state, fd, &metadata)
|
||||
{
|
||||
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,57 +1,15 @@
|
|||
use {
|
||||
crate::{
|
||||
format::Format,
|
||||
video::{
|
||||
Modifier,
|
||||
dmabuf::{DmaBuf, DmaBufIds},
|
||||
drm::Drm,
|
||||
},
|
||||
},
|
||||
std::{error::Error, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
pub use jay_allocator::*;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error(transparent)]
|
||||
pub struct AllocatorError(#[from] pub Box<dyn Error + Send>);
|
||||
use crate::video::drm::Drm;
|
||||
|
||||
bitflags! {
|
||||
BufferUsage: u32;
|
||||
BO_USE_SCANOUT,
|
||||
BO_USE_CURSOR,
|
||||
BO_USE_RENDERING,
|
||||
BO_USE_WRITE,
|
||||
BO_USE_LINEAR,
|
||||
BO_USE_PROTECTED,
|
||||
}
|
||||
|
||||
pub trait Allocator {
|
||||
fn drm(&self) -> Option<&Drm>;
|
||||
fn create_bo(
|
||||
&self,
|
||||
dma_buf_ids: &DmaBufIds,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: &'static Format,
|
||||
modifiers: &[Modifier],
|
||||
usage: BufferUsage,
|
||||
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
|
||||
fn import_dmabuf(
|
||||
&self,
|
||||
dmabuf: &DmaBuf,
|
||||
usage: BufferUsage,
|
||||
) -> Result<Rc<dyn BufferObject>, AllocatorError>;
|
||||
}
|
||||
|
||||
pub trait BufferObject {
|
||||
fn dmabuf(&self) -> &DmaBuf;
|
||||
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
|
||||
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError>;
|
||||
}
|
||||
|
||||
pub trait MappedBuffer {
|
||||
unsafe fn data(&self) -> &[u8];
|
||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
||||
fn data_ptr(&self) -> *mut u8;
|
||||
fn stride(&self) -> i32;
|
||||
impl AllocatorDrm for Drm {
|
||||
fn dev(&self) -> uapi::c::dev_t {
|
||||
self.dev()
|
||||
}
|
||||
|
||||
fn dup_render_fd(&self) -> Result<std::rc::Rc<uapi::OwnedFd>, AllocatorError> {
|
||||
self.dup_render()
|
||||
.map(|drm| drm.fd().clone())
|
||||
.map_err(|e| AllocatorError(Box::new(e)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
161
src/animation.rs
161
src/animation.rs
|
|
@ -19,127 +19,9 @@ use {
|
|||
|
||||
pub mod multiphase;
|
||||
|
||||
pub use jay_layout_animation::{AnimationCurve, AnimationStyle};
|
||||
|
||||
const DEFAULT_DURATION_MS: u32 = 160;
|
||||
const CURVE_MAX_POINTS: usize = 33;
|
||||
const CURVE_FLATNESS_EPSILON: f32 = 0.001;
|
||||
const CURVE_MAX_DEPTH: u8 = 8;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum AnimationCurve {
|
||||
Linear,
|
||||
Piecewise(PiecewiseCurve),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AnimationStyle {
|
||||
Plain,
|
||||
Multiphase,
|
||||
}
|
||||
|
||||
impl AnimationStyle {
|
||||
pub fn from_config(value: u32) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::Plain),
|
||||
1 => Some(Self::Multiphase),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationCurve {
|
||||
pub fn from_config(value: u32) -> Self {
|
||||
match value {
|
||||
0 => Self::Linear,
|
||||
1 => Self::from_cubic_bezier(0.25, 0.1, 0.25, 1.0).unwrap(),
|
||||
2 => Self::from_cubic_bezier(0.42, 0.0, 1.0, 1.0).unwrap(),
|
||||
4 => Self::from_cubic_bezier(0.42, 0.0, 0.58, 1.0).unwrap(),
|
||||
_ => Self::from_cubic_bezier(0.0, 0.0, 0.58, 1.0).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) -> Option<Self> {
|
||||
if !x1.is_finite()
|
||||
|| !y1.is_finite()
|
||||
|| !x2.is_finite()
|
||||
|| !y2.is_finite()
|
||||
|| !(0.0..=1.0).contains(&x1)
|
||||
|| !(0.0..=1.0).contains(&x2)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(Self::Piecewise(PiecewiseCurve::from_cubic_bezier(
|
||||
x1, y1, x2, y2,
|
||||
)))
|
||||
}
|
||||
|
||||
fn sample(self, t: f64) -> f64 {
|
||||
let t = t.clamp(0.0, 1.0);
|
||||
match self {
|
||||
Self::Linear => t,
|
||||
Self::Piecewise(curve) => curve.sample(t as f32) as f64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct PiecewiseCurve {
|
||||
len: u8,
|
||||
points: [CurvePoint; CURVE_MAX_POINTS],
|
||||
}
|
||||
|
||||
impl PiecewiseCurve {
|
||||
fn from_cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
|
||||
let mut points = Vec::with_capacity(CURVE_MAX_POINTS);
|
||||
let p0 = cubic_bezier_point(x1, y1, x2, y2, 0.0);
|
||||
let p1 = cubic_bezier_point(x1, y1, x2, y2, 1.0);
|
||||
points.push(p0);
|
||||
flatten_cubic_bezier(&mut points, (x1, y1, x2, y2), 0.0, p0, 1.0, p1, 0);
|
||||
let mut array = [CurvePoint::default(); CURVE_MAX_POINTS];
|
||||
let len = points.len().min(CURVE_MAX_POINTS);
|
||||
array[..len].copy_from_slice(&points[..len]);
|
||||
Self {
|
||||
len: len as u8,
|
||||
points: array,
|
||||
}
|
||||
}
|
||||
|
||||
fn sample(self, x: f32) -> f32 {
|
||||
let len = self.len as usize;
|
||||
if len <= 1 {
|
||||
return x;
|
||||
}
|
||||
let points = &self.points[..len];
|
||||
if x <= points[0].x {
|
||||
return points[0].y;
|
||||
}
|
||||
if x >= points[len - 1].x {
|
||||
return points[len - 1].y;
|
||||
}
|
||||
let mut lo = 0;
|
||||
let mut hi = len - 1;
|
||||
while lo + 1 < hi {
|
||||
let mid = (lo + hi) / 2;
|
||||
if points[mid].x <= x {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
let from = points[lo];
|
||||
let to = points[hi];
|
||||
if to.x <= from.x {
|
||||
return to.y;
|
||||
}
|
||||
let t = (x - from.x) / (to.x - from.x);
|
||||
from.y + (to.y - from.y) * t
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
struct CurvePoint {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
pub struct AnimationState {
|
||||
pub enabled: Cell<bool>,
|
||||
|
|
@ -885,45 +767,6 @@ pub(crate) fn expand_damage_rect(rect: Rect, width: i32) -> Rect {
|
|||
)
|
||||
}
|
||||
|
||||
fn flatten_cubic_bezier(
|
||||
points: &mut Vec<CurvePoint>,
|
||||
controls: (f32, f32, f32, f32),
|
||||
t0: f32,
|
||||
p0: CurvePoint,
|
||||
t1: f32,
|
||||
p1: CurvePoint,
|
||||
depth: u8,
|
||||
) {
|
||||
let tm = (t0 + t1) * 0.5;
|
||||
let pm = cubic_bezier_point(controls.0, controls.1, controls.2, controls.3, tm);
|
||||
let projected_y = if p1.x <= p0.x {
|
||||
(p0.y + p1.y) * 0.5
|
||||
} else {
|
||||
let tx = (pm.x - p0.x) / (p1.x - p0.x);
|
||||
p0.y + (p1.y - p0.y) * tx
|
||||
};
|
||||
if (pm.y - projected_y).abs() > CURVE_FLATNESS_EPSILON
|
||||
&& depth < CURVE_MAX_DEPTH
|
||||
&& points.len() + 2 < CURVE_MAX_POINTS
|
||||
{
|
||||
flatten_cubic_bezier(points, controls, t0, p0, tm, pm, depth + 1);
|
||||
flatten_cubic_bezier(points, controls, tm, pm, t1, p1, depth + 1);
|
||||
} else {
|
||||
points.push(p1);
|
||||
}
|
||||
}
|
||||
|
||||
fn cubic_bezier_point(x1: f32, y1: f32, x2: f32, y2: f32, t: f32) -> CurvePoint {
|
||||
fn bezier(a: f32, b: f32, t: f32) -> f32 {
|
||||
let inv = 1.0 - t;
|
||||
3.0 * inv * inv * t * a + 3.0 * inv * t * t * b + t * t * t
|
||||
}
|
||||
CurvePoint {
|
||||
x: bezier(x1, x2, t),
|
||||
y: bezier(y1, y2, t),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,170 +1 @@
|
|||
mod ae_task;
|
||||
mod ae_yield;
|
||||
|
||||
pub use {crate::async_engine::ae_yield::Yield, ae_task::SpawnedFuture};
|
||||
use {
|
||||
crate::{
|
||||
async_engine::ae_task::Runnable,
|
||||
time::Time,
|
||||
utils::{array, numcell::NumCell, syncqueue::SyncQueue},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::VecDeque,
|
||||
future::Future,
|
||||
rc::Rc,
|
||||
task::Waker,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Phase {
|
||||
EventHandling,
|
||||
Layout,
|
||||
PostLayout,
|
||||
Present,
|
||||
}
|
||||
const NUM_PHASES: usize = 4;
|
||||
|
||||
pub struct AsyncEngine {
|
||||
num_queued: NumCell<usize>,
|
||||
queues: [SyncQueue<Runnable>; NUM_PHASES],
|
||||
iteration: NumCell<u64>,
|
||||
yields: SyncQueue<Waker>,
|
||||
stash: RefCell<VecDeque<Runnable>>,
|
||||
yield_stash: RefCell<VecDeque<Waker>>,
|
||||
stopped: Cell<bool>,
|
||||
now: Cell<Option<Time>>,
|
||||
#[cfg(feature = "it")]
|
||||
idle: Cell<Option<Waker>>,
|
||||
}
|
||||
|
||||
impl AsyncEngine {
|
||||
pub fn new() -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
num_queued: Default::default(),
|
||||
queues: array::from_fn(|_| Default::default()),
|
||||
iteration: Default::default(),
|
||||
yields: Default::default(),
|
||||
stash: Default::default(),
|
||||
yield_stash: Default::default(),
|
||||
stopped: Cell::new(false),
|
||||
now: Default::default(),
|
||||
#[cfg(feature = "it")]
|
||||
idle: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stop(&self) {
|
||||
self.stopped.set(true);
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.stash.borrow_mut().clear();
|
||||
self.yield_stash.borrow_mut().clear();
|
||||
self.yields.take();
|
||||
for queue in &self.queues {
|
||||
queue.take();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn<T, F: Future<Output = T> + 'static>(
|
||||
self: &Rc<Self>,
|
||||
name: &str,
|
||||
f: F,
|
||||
) -> SpawnedFuture<T> {
|
||||
self.spawn_(name, Phase::EventHandling, f)
|
||||
}
|
||||
|
||||
pub fn spawn2<T, F: Future<Output = T> + 'static>(
|
||||
self: &Rc<Self>,
|
||||
name: &str,
|
||||
phase: Phase,
|
||||
f: F,
|
||||
) -> SpawnedFuture<T> {
|
||||
self.spawn_(name, phase, f)
|
||||
}
|
||||
|
||||
pub fn yield_now(self: &Rc<Self>) -> Yield {
|
||||
Yield {
|
||||
iteration: self.iteration(),
|
||||
queue: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch(&self) {
|
||||
let mut stash = self.stash.borrow_mut();
|
||||
let mut yield_stash = self.yield_stash.borrow_mut();
|
||||
loop {
|
||||
if self.num_queued.get() == 0 {
|
||||
#[cfg(feature = "it")]
|
||||
if let Some(idle) = self.idle.take() {
|
||||
idle.wake();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
self.now.take();
|
||||
let mut phase = 0;
|
||||
while phase < NUM_PHASES {
|
||||
self.queues[phase].swap(&mut *stash);
|
||||
if stash.is_empty() {
|
||||
phase += 1;
|
||||
continue;
|
||||
}
|
||||
self.num_queued.fetch_sub(stash.len());
|
||||
while let Some(runnable) = stash.pop_front() {
|
||||
runnable.run();
|
||||
if self.stopped.get() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.iteration.fetch_add(1);
|
||||
self.yields.swap(&mut *yield_stash);
|
||||
while let Some(waker) = yield_stash.pop_front() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "it")]
|
||||
pub async fn idle(&self) {
|
||||
use std::{future::poll_fn, task::Poll};
|
||||
let mut register = true;
|
||||
poll_fn(|ctx| {
|
||||
if register {
|
||||
self.idle.set(Some(ctx.waker().clone()));
|
||||
register = false;
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn push(&self, runnable: Runnable, phase: Phase) {
|
||||
self.queues[phase as usize].push(runnable);
|
||||
self.num_queued.fetch_add(1);
|
||||
}
|
||||
|
||||
fn push_yield(&self, waker: Waker) {
|
||||
self.yields.push(waker);
|
||||
}
|
||||
|
||||
pub fn iteration(&self) -> u64 {
|
||||
self.iteration.get()
|
||||
}
|
||||
|
||||
pub fn now(&self) -> Time {
|
||||
match self.now.get() {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
let now = Time::now_unchecked();
|
||||
self.now.set(Some(now));
|
||||
now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use jay_async_engine::*;
|
||||
|
|
|
|||
|
|
@ -1,290 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, Phase},
|
||||
tracy::ZoneName,
|
||||
utils::{
|
||||
numcell::NumCell,
|
||||
ptr_ext::{MutPtrExt, PtrExt},
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
future::Future,
|
||||
mem::ManuallyDrop,
|
||||
pin::Pin,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||
},
|
||||
};
|
||||
|
||||
#[must_use]
|
||||
pub struct SpawnedFuture<T: 'static> {
|
||||
vtable: &'static SpawnedFutureVtable<T>,
|
||||
data: *mut u8,
|
||||
}
|
||||
|
||||
impl<T> Future for SpawnedFuture<T> {
|
||||
type Output = T;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
unsafe { (self.vtable.poll)(self.data, cx) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for SpawnedFuture<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.vtable.drop)(self.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SpawnedFutureVTableProxy<T, F>(T, F);
|
||||
|
||||
impl<T: 'static, F: Future<Output = T>> SpawnedFutureVTableProxy<T, F> {
|
||||
const VTABLE: &'static SpawnedFutureVtable<T> = &SpawnedFutureVtable {
|
||||
poll: Self::poll,
|
||||
drop: Self::drop,
|
||||
};
|
||||
|
||||
unsafe fn poll(data: *mut u8, ctx: &mut Context<'_>) -> Poll<T> {
|
||||
unsafe {
|
||||
let task = (data as *const Task<T, F>).deref();
|
||||
if &task.state & COMPLETED == 0 {
|
||||
task.waker.set(Some(ctx.waker().clone()));
|
||||
Poll::Pending
|
||||
} else if &task.state & EMPTIED == 0 {
|
||||
task.state.or_assign(EMPTIED);
|
||||
Poll::Ready(ptr::read(&*task.data.get().deref().result))
|
||||
} else {
|
||||
panic!("Future polled after it has already been emptied");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn drop(data: *mut u8) {
|
||||
unsafe {
|
||||
{
|
||||
let task = (data as *const Task<T, F>).deref();
|
||||
task.state.or_assign(CANCELLED);
|
||||
if &task.state & RUNNING == 0 {
|
||||
task.drop_data();
|
||||
}
|
||||
}
|
||||
Task::<T, F>::dec_ref_count(data as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SpawnedFutureVtable<T> {
|
||||
poll: unsafe fn(data: *mut u8, ctx: &mut Context<'_>) -> Poll<T>,
|
||||
drop: unsafe fn(data: *mut u8),
|
||||
}
|
||||
|
||||
union TaskData<T, F: Future<Output = T>> {
|
||||
result: ManuallyDrop<T>,
|
||||
future: ManuallyDrop<F>,
|
||||
}
|
||||
|
||||
const RUNNING: u32 = 1;
|
||||
const RUN_AGAIN: u32 = 2;
|
||||
const COMPLETED: u32 = 4;
|
||||
const EMPTIED: u32 = 8;
|
||||
const CANCELLED: u32 = 16;
|
||||
|
||||
struct Task<T, F: Future<Output = T>> {
|
||||
ref_count: NumCell<u64>,
|
||||
phase: Phase,
|
||||
state: NumCell<u32>,
|
||||
data: UnsafeCell<TaskData<T, F>>,
|
||||
waker: Cell<Option<Waker>>,
|
||||
queue: Rc<AsyncEngine>,
|
||||
#[cfg_attr(not(feature = "tracy"), expect(dead_code))]
|
||||
zone: ZoneName,
|
||||
}
|
||||
|
||||
pub(super) struct Runnable {
|
||||
data: *const u8,
|
||||
run: unsafe fn(data: *const u8, run: bool),
|
||||
}
|
||||
|
||||
impl Runnable {
|
||||
pub(super) fn run(self) {
|
||||
let slf = ManuallyDrop::new(self);
|
||||
unsafe {
|
||||
(slf.run)(slf.data, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Runnable {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.run)(self.data, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncEngine {
|
||||
pub(super) fn spawn_<T, F: Future<Output = T>>(
|
||||
self: &Rc<Self>,
|
||||
#[cfg_attr(not(feature = "tracy"), expect(unused_variables))] name: &str,
|
||||
phase: Phase,
|
||||
f: F,
|
||||
) -> SpawnedFuture<T> {
|
||||
let f = Box::new(Task {
|
||||
ref_count: NumCell::new(1),
|
||||
phase,
|
||||
state: NumCell::new(0),
|
||||
data: UnsafeCell::new(TaskData {
|
||||
future: ManuallyDrop::new(f),
|
||||
}),
|
||||
waker: Cell::new(None),
|
||||
queue: self.clone(),
|
||||
zone: create_zone_name!("task:{}", name),
|
||||
});
|
||||
unsafe {
|
||||
f.schedule_run();
|
||||
}
|
||||
let f = Box::into_raw(f);
|
||||
SpawnedFuture {
|
||||
vtable: SpawnedFutureVTableProxy::<T, F>::VTABLE,
|
||||
data: f as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F: Future<Output = T>> Task<T, F> {
|
||||
const VTABLE: &'static RawWakerVTable = &RawWakerVTable::new(
|
||||
Self::waker_clone,
|
||||
Self::waker_wake,
|
||||
Self::waker_wake_by_ref,
|
||||
Self::waker_drop,
|
||||
);
|
||||
|
||||
unsafe fn run_proxy(data: *const u8, run: bool) {
|
||||
unsafe {
|
||||
let task = data as *const Self;
|
||||
if run {
|
||||
task.deref().run();
|
||||
} else {
|
||||
Self::task_runnable_dropped(task);
|
||||
}
|
||||
Self::dec_ref_count(task);
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn task_runnable_dropped(task: *const Self) {
|
||||
unsafe {
|
||||
let task = task.deref();
|
||||
task.state.and_assign(!RUNNING);
|
||||
if task.state.get() & CANCELLED != 0 {
|
||||
task.drop_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dec_ref_count(slf: *const Self) {
|
||||
unsafe {
|
||||
if slf.deref().ref_count.fetch_sub(1) == 1 {
|
||||
drop(Box::from_raw(slf as *mut Self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn inc_ref_count(&self) {
|
||||
self.ref_count.fetch_add(1);
|
||||
}
|
||||
|
||||
unsafe fn waker_clone(data: *const ()) -> RawWaker {
|
||||
unsafe {
|
||||
let task = &mut *(data as *mut Self);
|
||||
task.inc_ref_count();
|
||||
RawWaker::new(data, Self::VTABLE)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn waker_wake(data: *const ()) {
|
||||
unsafe {
|
||||
Self::waker_wake_by_ref(data);
|
||||
Self::waker_drop(data);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn waker_wake_by_ref(data: *const ()) {
|
||||
unsafe {
|
||||
(data as *const Self).deref().schedule_run();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn waker_drop(data: *const ()) {
|
||||
unsafe { Self::dec_ref_count(data as _) }
|
||||
}
|
||||
|
||||
unsafe fn schedule_run(&self) {
|
||||
unsafe {
|
||||
if &self.state & (COMPLETED | CANCELLED) == 0 {
|
||||
if &self.state & RUNNING == 0 {
|
||||
self.state.or_assign(RUNNING);
|
||||
self.inc_ref_count();
|
||||
let data = self as *const _ as _;
|
||||
self.queue.push(
|
||||
Runnable {
|
||||
data,
|
||||
run: Self::run_proxy,
|
||||
},
|
||||
self.phase,
|
||||
);
|
||||
} else {
|
||||
self.state.or_assign(RUN_AGAIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn run(&self) {
|
||||
unsafe {
|
||||
if &self.state & CANCELLED == 0 {
|
||||
let data = self.data.get().deref_mut();
|
||||
self.inc_ref_count();
|
||||
let raw_waker = RawWaker::new(self as *const _ as _, Self::VTABLE);
|
||||
let waker = Waker::from_raw(raw_waker);
|
||||
|
||||
let mut ctx = Context::from_waker(&waker);
|
||||
let poll = {
|
||||
dynamic_zone!(self.zone);
|
||||
Pin::new_unchecked(&mut *data.future).poll(&mut ctx)
|
||||
};
|
||||
if let Poll::Ready(d) = poll {
|
||||
ManuallyDrop::drop(&mut data.future);
|
||||
ptr::write(&mut data.result, ManuallyDrop::new(d));
|
||||
self.state.or_assign(COMPLETED);
|
||||
if let Some(waker) = self.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.state.and_assign(!RUNNING);
|
||||
|
||||
if &self.state & CANCELLED != 0 {
|
||||
self.drop_data();
|
||||
} else if &self.state & RUN_AGAIN != 0 {
|
||||
self.state.and_assign(!RUN_AGAIN);
|
||||
self.schedule_run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn drop_data(&self) {
|
||||
unsafe {
|
||||
if &self.state & COMPLETED == 0 {
|
||||
ManuallyDrop::drop(&mut self.data.get().deref_mut().future);
|
||||
} else if &self.state & EMPTIED == 0 {
|
||||
ManuallyDrop::drop(&mut self.data.get().deref_mut().result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
use {
|
||||
crate::async_engine::AsyncEngine,
|
||||
std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
task::{Context, Poll},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Yield {
|
||||
pub(super) iteration: u64,
|
||||
pub(super) queue: Rc<AsyncEngine>,
|
||||
}
|
||||
|
||||
impl Future for Yield {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.queue.iteration() > self.iteration {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.queue.push_yield(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
498
src/backend.rs
498
src/backend.rs
|
|
@ -5,47 +5,40 @@ use {
|
|||
BackendConnectorTransaction, BackendConnectorTransactionError,
|
||||
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
format::Format,
|
||||
gfx_api::{FdSync, GfxApi, GfxFramebuffer},
|
||||
ifs::{
|
||||
wl_output::OutputId,
|
||||
wl_seat::{
|
||||
tablet::{
|
||||
PadButtonState, TabletInit, TabletPadId, TabletPadInit, TabletRingEventSource,
|
||||
TabletStripEventSource, TabletToolChanges, TabletToolId, TabletToolInit,
|
||||
ToolButtonState,
|
||||
},
|
||||
wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||
},
|
||||
},
|
||||
libinput::consts::DeviceCapability,
|
||||
utils::static_text::StaticText,
|
||||
video::drm::{
|
||||
ConnectorType, DRM_MODE_COLORIMETRY_BT2020_RGB, DRM_MODE_COLORIMETRY_DEFAULT,
|
||||
DrmConnector, DrmError, DrmVersion, HDMI_EOTF_SMPTE_ST2084,
|
||||
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
|
||||
},
|
||||
video::drm::{ConnectorType, DrmError, DrmVersion},
|
||||
},
|
||||
jay_config::input::SwitchEvent,
|
||||
linearize::Linearize,
|
||||
jay_video_types::drm::DrmConnector,
|
||||
std::{
|
||||
any::Any,
|
||||
error::Error,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
hash::Hash,
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::{OwnedFd, Packed, Pod, c},
|
||||
uapi::{OwnedFd, c},
|
||||
};
|
||||
|
||||
pub mod transaction;
|
||||
|
||||
linear_ids!(ConnectorIds, ConnectorId);
|
||||
linear_ids!(InputDeviceIds, InputDeviceId);
|
||||
linear_ids!(DrmDeviceIds, DrmDeviceId);
|
||||
pub use jay_output_types::{
|
||||
BackendColorSpace, BackendConnectorState, BackendConnectorStateSerial,
|
||||
BackendConnectorStateSerials, BackendEotfs, BackendGammaLut, BackendGammaLutElement,
|
||||
BackendLuminance, CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY,
|
||||
ConnectorCaps, ConnectorId, ConnectorIds, DrmDeviceId, DrmDeviceIds, Mode, MonitorInfo,
|
||||
OutputId,
|
||||
};
|
||||
|
||||
pub use jay_input_types::{
|
||||
AXIS_120, AxisSource, ButtonState, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceGroupIds, InputDeviceId,
|
||||
InputDeviceIds, InputEvent, KeyState, Leds, PadButtonState, ScrollAxis, SwitchEvent, TabletId,
|
||||
TabletIds, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadIds, TabletPadInit,
|
||||
TabletRingEventSource, TabletStripEventSource, TabletTool2dChange, TabletToolCapability,
|
||||
TabletToolChanges, TabletToolId, TabletToolIds, TabletToolInit, TabletToolPositionChange,
|
||||
TabletToolType, TabletToolWheelChange, ToolButtonState, TransformMatrix,
|
||||
};
|
||||
|
||||
pub trait Backend: Any {
|
||||
fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>>;
|
||||
|
|
@ -70,54 +63,6 @@ pub trait Backend: Any {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
||||
pub struct Mode {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub refresh_rate_millihz: u32,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn refresh_nsec(&self) -> u64 {
|
||||
match self.refresh_rate_millihz {
|
||||
0 => u64::MAX,
|
||||
n => 1_000_000_000_000 / (n as u64),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (i32, i32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Mode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}x{}@{}",
|
||||
self.width,
|
||||
self.height,
|
||||
self.refresh_rate_millihz as f64 / 1000.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MonitorInfo {
|
||||
pub modes: Option<Vec<Mode>>,
|
||||
pub output_id: Rc<OutputId>,
|
||||
pub width_mm: i32,
|
||||
pub height_mm: i32,
|
||||
pub non_desktop: bool,
|
||||
pub non_desktop_effective: bool,
|
||||
pub vrr_capable: bool,
|
||||
pub eotfs: Vec<BackendEotfs>,
|
||||
pub color_spaces: Vec<BackendColorSpace>,
|
||||
pub primaries: Primaries,
|
||||
pub luminance: Option<BackendLuminance>,
|
||||
pub state: BackendConnectorState,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ConnectorKernelId {
|
||||
pub ty: ConnectorType,
|
||||
|
|
@ -130,13 +75,6 @@ impl Display for ConnectorKernelId {
|
|||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
ConnectorCaps: u32;
|
||||
CONCAP_CONNECTOR,
|
||||
CONCAP_MODE_SETTING,
|
||||
CONCAP_PHYSICAL_DISPLAY,
|
||||
}
|
||||
|
||||
pub trait Connector: Any {
|
||||
fn id(&self) -> ConnectorId;
|
||||
fn kernel_id(&self) -> ConnectorKernelId;
|
||||
|
|
@ -203,10 +141,6 @@ pub trait HardwareCursor: Debug {
|
|||
fn damage(&self);
|
||||
}
|
||||
|
||||
pub type TransformMatrix = [[f64; 2]; 2];
|
||||
|
||||
linear_ids!(InputDeviceGroupIds, InputDeviceGroupId, usize);
|
||||
|
||||
pub trait InputDevice {
|
||||
fn id(&self) -> InputDeviceId;
|
||||
fn removed(&self) -> bool;
|
||||
|
|
@ -276,78 +210,6 @@ pub trait InputDevice {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Linearize)]
|
||||
pub enum InputDeviceCapability {
|
||||
Keyboard,
|
||||
Pointer,
|
||||
Touch,
|
||||
TabletTool,
|
||||
TabletPad,
|
||||
Gesture,
|
||||
Switch,
|
||||
}
|
||||
|
||||
impl StaticText for InputDeviceCapability {
|
||||
fn text(&self) -> &'static str {
|
||||
match self {
|
||||
InputDeviceCapability::Keyboard => "keyboard",
|
||||
InputDeviceCapability::Pointer => "pointer",
|
||||
InputDeviceCapability::Touch => "touch",
|
||||
InputDeviceCapability::TabletTool => "tablet tool",
|
||||
InputDeviceCapability::TabletPad => "tablet pad",
|
||||
InputDeviceCapability::Gesture => "gesture",
|
||||
InputDeviceCapability::Switch => "switch",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputDeviceCapability {
|
||||
pub fn to_libinput(self) -> DeviceCapability {
|
||||
use crate::libinput::consts::*;
|
||||
match self {
|
||||
InputDeviceCapability::Keyboard => LIBINPUT_DEVICE_CAP_KEYBOARD,
|
||||
InputDeviceCapability::Pointer => LIBINPUT_DEVICE_CAP_POINTER,
|
||||
InputDeviceCapability::Touch => LIBINPUT_DEVICE_CAP_TOUCH,
|
||||
InputDeviceCapability::TabletTool => LIBINPUT_DEVICE_CAP_TABLET_TOOL,
|
||||
InputDeviceCapability::TabletPad => LIBINPUT_DEVICE_CAP_TABLET_PAD,
|
||||
InputDeviceCapability::Gesture => LIBINPUT_DEVICE_CAP_GESTURE,
|
||||
InputDeviceCapability::Switch => LIBINPUT_DEVICE_CAP_SWITCH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Linearize)]
|
||||
pub enum InputDeviceAccelProfile {
|
||||
Flat,
|
||||
Adaptive,
|
||||
}
|
||||
|
||||
impl StaticText for InputDeviceAccelProfile {
|
||||
fn text(&self) -> &'static str {
|
||||
match self {
|
||||
InputDeviceAccelProfile::Flat => "Flat",
|
||||
InputDeviceAccelProfile::Adaptive => "Adaptive",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Linearize)]
|
||||
pub enum InputDeviceClickMethod {
|
||||
None,
|
||||
ButtonAreas,
|
||||
Clickfinger,
|
||||
}
|
||||
|
||||
impl StaticText for InputDeviceClickMethod {
|
||||
fn text(&self) -> &'static str {
|
||||
match self {
|
||||
InputDeviceClickMethod::None => "none",
|
||||
InputDeviceClickMethod::ButtonAreas => "button-areas",
|
||||
InputDeviceClickMethod::Clickfinger => "clickfinger",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BackendEvent {
|
||||
NewDrmDevice(Rc<dyn BackendDrmDevice>),
|
||||
NewConnector(Rc<dyn Connector>),
|
||||
|
|
@ -355,216 +217,6 @@ pub enum BackendEvent {
|
|||
DevicesEnumerated,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum KeyState {
|
||||
Released,
|
||||
Pressed,
|
||||
Repeated,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ButtonState {
|
||||
Released,
|
||||
Pressed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Linearize)]
|
||||
pub enum ScrollAxis {
|
||||
Horizontal = HORIZONTAL_SCROLL as _,
|
||||
Vertical = VERTICAL_SCROLL as _,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum AxisSource {
|
||||
Wheel = WHEEL as _,
|
||||
Finger = FINGER as _,
|
||||
Continuous = CONTINUOUS as _,
|
||||
}
|
||||
|
||||
pub const AXIS_120: i32 = 120;
|
||||
|
||||
bitflags! {
|
||||
Leds: u32;
|
||||
LED_NUM_LOCK,
|
||||
LED_CAPS_LOCK,
|
||||
LED_SCROLL_LOCK,
|
||||
LED_COMPOSE,
|
||||
LED_KANA,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InputEvent {
|
||||
Key {
|
||||
time_usec: u64,
|
||||
key: u32,
|
||||
state: KeyState,
|
||||
},
|
||||
ConnectorPosition {
|
||||
time_usec: u64,
|
||||
connector: ConnectorId,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
},
|
||||
Motion {
|
||||
time_usec: u64,
|
||||
dx: Fixed,
|
||||
dy: Fixed,
|
||||
dx_unaccelerated: Fixed,
|
||||
dy_unaccelerated: Fixed,
|
||||
},
|
||||
MotionAbsolute {
|
||||
time_usec: u64,
|
||||
x_normed: f32,
|
||||
y_normed: f32,
|
||||
},
|
||||
Button {
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ButtonState,
|
||||
},
|
||||
|
||||
AxisPx {
|
||||
dist: Fixed,
|
||||
axis: ScrollAxis,
|
||||
inverted: bool,
|
||||
},
|
||||
AxisSource {
|
||||
source: AxisSource,
|
||||
},
|
||||
AxisStop {
|
||||
axis: ScrollAxis,
|
||||
},
|
||||
Axis120 {
|
||||
dist: i32,
|
||||
axis: ScrollAxis,
|
||||
inverted: bool,
|
||||
},
|
||||
AxisFrame {
|
||||
time_usec: u64,
|
||||
},
|
||||
SwipeBegin {
|
||||
time_usec: u64,
|
||||
finger_count: u32,
|
||||
},
|
||||
SwipeUpdate {
|
||||
time_usec: u64,
|
||||
dx: Fixed,
|
||||
dy: Fixed,
|
||||
dx_unaccelerated: Fixed,
|
||||
dy_unaccelerated: Fixed,
|
||||
},
|
||||
SwipeEnd {
|
||||
time_usec: u64,
|
||||
cancelled: bool,
|
||||
},
|
||||
PinchBegin {
|
||||
time_usec: u64,
|
||||
finger_count: u32,
|
||||
},
|
||||
PinchUpdate {
|
||||
time_usec: u64,
|
||||
dx: Fixed,
|
||||
dy: Fixed,
|
||||
dx_unaccelerated: Fixed,
|
||||
dy_unaccelerated: Fixed,
|
||||
scale: Fixed,
|
||||
rotation: Fixed,
|
||||
},
|
||||
PinchEnd {
|
||||
time_usec: u64,
|
||||
cancelled: bool,
|
||||
},
|
||||
HoldBegin {
|
||||
time_usec: u64,
|
||||
finger_count: u32,
|
||||
},
|
||||
HoldEnd {
|
||||
time_usec: u64,
|
||||
cancelled: bool,
|
||||
},
|
||||
|
||||
SwitchEvent {
|
||||
time_usec: u64,
|
||||
event: SwitchEvent,
|
||||
},
|
||||
|
||||
TabletToolAdded {
|
||||
time_usec: u64,
|
||||
init: Box<TabletToolInit>,
|
||||
},
|
||||
TabletToolChanged {
|
||||
time_usec: u64,
|
||||
id: TabletToolId,
|
||||
changes: Box<TabletToolChanges>,
|
||||
},
|
||||
TabletToolButton {
|
||||
time_usec: u64,
|
||||
id: TabletToolId,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
},
|
||||
TabletToolRemoved {
|
||||
time_usec: u64,
|
||||
id: TabletToolId,
|
||||
},
|
||||
|
||||
TabletPadButton {
|
||||
time_usec: u64,
|
||||
id: TabletPadId,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
},
|
||||
TabletPadModeSwitch {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
group: u32,
|
||||
mode: u32,
|
||||
},
|
||||
TabletPadRing {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
ring: u32,
|
||||
source: Option<TabletRingEventSource>,
|
||||
angle: Option<f64>,
|
||||
},
|
||||
TabletPadStrip {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
strip: u32,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
},
|
||||
TabletPadDial {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
dial: u32,
|
||||
value120: i32,
|
||||
},
|
||||
TouchDown {
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
x_normed: Fixed,
|
||||
y_normed: Fixed,
|
||||
},
|
||||
TouchUp {
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
},
|
||||
TouchMotion {
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
x_normed: Fixed,
|
||||
y_normed: Fixed,
|
||||
},
|
||||
TouchCancel {
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
},
|
||||
TouchFrame {
|
||||
time_usec: u64,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum DrmEvent {
|
||||
#[expect(dead_code)]
|
||||
Removed,
|
||||
|
|
@ -608,113 +260,3 @@ pub trait BackendDrmLease {
|
|||
pub trait BackendDrmLessee {
|
||||
fn created(&self, lease: Rc<dyn BackendDrmLease>);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Linearize)]
|
||||
pub enum BackendEotfs {
|
||||
#[default]
|
||||
Default,
|
||||
Pq,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Linearize)]
|
||||
pub enum BackendColorSpace {
|
||||
#[default]
|
||||
Default,
|
||||
Bt2020,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BackendLuminance {
|
||||
pub min: f64,
|
||||
pub max: f64,
|
||||
pub max_fall: f64,
|
||||
}
|
||||
|
||||
impl BackendEotfs {
|
||||
pub fn to_drm(self) -> u8 {
|
||||
match self {
|
||||
BackendEotfs::Default => HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
|
||||
BackendEotfs::Pq => HDMI_EOTF_SMPTE_ST2084,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn name(self) -> &'static str {
|
||||
match self {
|
||||
BackendEotfs::Default => "default",
|
||||
BackendEotfs::Pq => "pq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendColorSpace {
|
||||
pub fn to_drm(self) -> u64 {
|
||||
match self {
|
||||
BackendColorSpace::Default => DRM_MODE_COLORIMETRY_DEFAULT,
|
||||
BackendColorSpace::Bt2020 => DRM_MODE_COLORIMETRY_BT2020_RGB,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn name(self) -> &'static str {
|
||||
match self {
|
||||
BackendColorSpace::Default => "default",
|
||||
BackendColorSpace::Bt2020 => "bt2020",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// kernel: struct drm_color_lut
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
||||
#[repr(C)]
|
||||
pub struct BackendGammaLutElement {
|
||||
pub red: u16,
|
||||
pub green: u16,
|
||||
pub blue: u16,
|
||||
pub reserved: u16,
|
||||
}
|
||||
|
||||
unsafe impl Pod for BackendGammaLutElement {}
|
||||
unsafe impl Packed for BackendGammaLutElement {}
|
||||
|
||||
#[derive(Debug, Eq)]
|
||||
pub struct BackendGammaLut {
|
||||
id: [u8; 32],
|
||||
pub gamma_lut: Vec<BackendGammaLutElement>,
|
||||
}
|
||||
|
||||
impl BackendGammaLut {
|
||||
pub fn new(mut gamma_lut: Vec<BackendGammaLutElement>) -> Self {
|
||||
for element in &mut gamma_lut {
|
||||
element.reserved = 0;
|
||||
}
|
||||
let gamma_lut_bytes = uapi::as_bytes(&gamma_lut as &[_]);
|
||||
let id = *blake3::hash(gamma_lut_bytes).as_bytes();
|
||||
Self { id, gamma_lut }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BackendGammaLut {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
linear_ids!(
|
||||
BackendConnectorStateSerials,
|
||||
BackendConnectorStateSerial,
|
||||
u64
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct BackendConnectorState {
|
||||
pub serial: BackendConnectorStateSerial,
|
||||
pub enabled: bool,
|
||||
pub active: bool,
|
||||
pub mode: Mode,
|
||||
pub non_desktop_override: Option<bool>,
|
||||
pub vrr: bool,
|
||||
pub tearing: bool,
|
||||
pub format: &'static Format,
|
||||
pub color_space: BackendColorSpace,
|
||||
pub eotf: BackendEotfs,
|
||||
pub gamma_lut: Option<Rc<BackendGammaLut>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use {
|
|||
backend::{
|
||||
Backend, ButtonState, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, Leds,
|
||||
OutputId, TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
|
||||
TransformMatrix, transaction::BackendConnectorTransactionError,
|
||||
},
|
||||
backends::metal::{
|
||||
|
|
@ -23,12 +24,6 @@ use {
|
|||
dbus::{DbusError, SignalHandler},
|
||||
drm_feedback::DrmFeedback,
|
||||
gfx_api::{GfxError, SyncFile},
|
||||
ifs::{
|
||||
wl_output::OutputId,
|
||||
wl_seat::tablet::{
|
||||
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
|
||||
},
|
||||
},
|
||||
libinput::{
|
||||
LibInput, LibInputAdapter, LibInputError,
|
||||
consts::{
|
||||
|
|
@ -548,7 +543,7 @@ impl InputDevice for MetalInputDevice {
|
|||
}
|
||||
|
||||
fn has_capability(&self, cap: InputDeviceCapability) -> bool {
|
||||
let li = cap.to_libinput();
|
||||
let li = crate::libinput::device_capability(cap);
|
||||
match self.inputdev.get() {
|
||||
Some(dev) => dev.device().has_cap(li),
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{AxisSource, ButtonState, InputEvent, KeyState, ScrollAxis},
|
||||
backends::metal::MetalBackend,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::tablet::{
|
||||
PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange,
|
||||
backend::{
|
||||
AxisSource, ButtonState, InputEvent, KeyState, PadButtonState, ScrollAxis,
|
||||
SwitchEvent, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange,
|
||||
TabletToolCapability, TabletToolChanges, TabletToolId, TabletToolInit,
|
||||
TabletToolPositionChange, TabletToolType, TabletToolWheelChange, ToolButtonState,
|
||||
},
|
||||
backends::metal::MetalBackend,
|
||||
fixed::Fixed,
|
||||
libinput::{
|
||||
consts::{
|
||||
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_BUTTON_STATE_RELEASED,
|
||||
|
|
@ -25,7 +25,6 @@ use {
|
|||
},
|
||||
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt},
|
||||
},
|
||||
jay_config::input::SwitchEvent,
|
||||
std::rc::Rc,
|
||||
uapi::c,
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
44
src/backends/metal/video/copy_device.rs
Normal file
44
src/backends/metal/video/copy_device.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use {
|
||||
crate::{
|
||||
copy_device::{CopyDevice, CopyDeviceRegistry},
|
||||
utils::errorfmt::ErrorFmt,
|
||||
},
|
||||
std::{
|
||||
cell::OnceCell,
|
||||
fmt::{Debug, Formatter},
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::c::dev_t,
|
||||
};
|
||||
|
||||
pub struct CopyDeviceHolder {
|
||||
pub registry: Rc<CopyDeviceRegistry>,
|
||||
pub devnum: dev_t,
|
||||
pub dev: OnceCell<Option<Rc<CopyDevice>>>,
|
||||
}
|
||||
|
||||
impl Debug for CopyDeviceHolder {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("CopyDeviceHolder").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyDeviceHolder {
|
||||
pub fn get(&self) -> Option<Rc<CopyDevice>> {
|
||||
self.dev
|
||||
.get_or_init(
|
||||
|| match self.registry.get(self.devnum)?.create_device().map(Some) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not get copy device for {}: {}",
|
||||
self.devnum,
|
||||
ErrorFmt(e),
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
660
src/backends/metal/video/discovery.rs
Normal file
660
src/backends/metal/video/discovery.rs
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
use {
|
||||
super::{
|
||||
ConnectorDisplayData, ConnectorFutures, FrontState, MetalConnector, MetalCrtc,
|
||||
MetalDrmDevice, MetalEncoder, MetalPlane, PersistentDisplayData, PlaneFormat, PlaneType,
|
||||
properties::{DefaultValue, collect_properties, create_default_properties},
|
||||
},
|
||||
crate::{
|
||||
async_engine::Phase,
|
||||
backend::{
|
||||
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendLuminance,
|
||||
ConnectorKernelId, Mode, OutputId,
|
||||
},
|
||||
backends::metal::{
|
||||
MetalBackend,
|
||||
present::DEFAULT_PRE_COMMIT_MARGIN,
|
||||
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState},
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||
format::XRGB8888,
|
||||
utils::{
|
||||
binary_search_map::BinarySearchMap, bitflags::BitflagsExt, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, geometric_decay::GeometricDecay,
|
||||
numcell::NumCell, ordered_float::F64,
|
||||
},
|
||||
video::{
|
||||
INVALID_MODIFIER,
|
||||
drm::{
|
||||
ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, DrmEncoder,
|
||||
DrmError, DrmFb, DrmMaster, DrmObject, DrmPlane, DrmPropertyType,
|
||||
HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, hdr_output_metadata,
|
||||
},
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
bstr::ByteSlice,
|
||||
indexmap::indexset,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
pub(super) fn get_connectors(
|
||||
backend: &Rc<MetalBackend>,
|
||||
dev: &Rc<MetalDrmDevice>,
|
||||
ids: &[DrmConnector],
|
||||
) -> Result<
|
||||
(
|
||||
CopyHashMap<DrmConnector, Rc<MetalConnector>>,
|
||||
CopyHashMap<DrmConnector, ConnectorFutures>,
|
||||
),
|
||||
DrmError,
|
||||
> {
|
||||
let connectors = CopyHashMap::new();
|
||||
let futures = CopyHashMap::new();
|
||||
for connector in ids {
|
||||
match create_connector(backend, *connector, dev) {
|
||||
Ok((con, fut)) => {
|
||||
let id = con.id;
|
||||
connectors.set(id, con);
|
||||
futures.set(id, fut);
|
||||
}
|
||||
Err(e) => return Err(DrmError::CreateConnector(Box::new(e))),
|
||||
}
|
||||
}
|
||||
Ok((connectors, futures))
|
||||
}
|
||||
|
||||
pub(super) fn create_connector(
|
||||
backend: &Rc<MetalBackend>,
|
||||
connector: DrmConnector,
|
||||
dev: &Rc<MetalDrmDevice>,
|
||||
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
|
||||
let display = create_connector_display_data(connector, dev)?;
|
||||
log::info!(
|
||||
"Creating connector {} for device {}",
|
||||
display.connector_id,
|
||||
dev.devnode.as_bytes().as_bstr(),
|
||||
);
|
||||
let slf = Rc::new(MetalConnector {
|
||||
id: connector,
|
||||
kernel_id: Cell::new(display.connector_id),
|
||||
master: dev.master.clone(),
|
||||
state: backend.state.clone(),
|
||||
dev: dev.clone(),
|
||||
backend: backend.clone(),
|
||||
connector_id: backend.state.connector_ids.next(),
|
||||
buffers: Default::default(),
|
||||
color_description: CloneCell::new(backend.state.color_manager.srgb_gamma22().clone()),
|
||||
lease: Cell::new(None),
|
||||
buffers_idle: Cell::new(true),
|
||||
crtc_idle: Cell::new(true),
|
||||
has_damage: NumCell::new(1),
|
||||
primary_plane: Default::default(),
|
||||
cursor_plane: Default::default(),
|
||||
crtc: Default::default(),
|
||||
on_change: Default::default(),
|
||||
present_trigger: Default::default(),
|
||||
cursor_x: Cell::new(0),
|
||||
cursor_y: Cell::new(0),
|
||||
cursor_enabled: Cell::new(false),
|
||||
cursor_buffers: Default::default(),
|
||||
display: RefCell::new(display),
|
||||
frontend_state: Cell::new(FrontState::Removed),
|
||||
cursor_changed: Cell::new(false),
|
||||
cursor_damage: Cell::new(false),
|
||||
cursor_swap_buffer: Cell::new(false),
|
||||
cursor_sync: Default::default(),
|
||||
drm_feedback: Default::default(),
|
||||
scanout_buffers: Default::default(),
|
||||
active_framebuffer: Default::default(),
|
||||
next_framebuffer: Default::default(),
|
||||
direct_scanout_active: Cell::new(false),
|
||||
next_vblank_nsec: Cell::new(0),
|
||||
version: Default::default(),
|
||||
expected_sequence: Default::default(),
|
||||
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
|
||||
pre_commit_margin: Cell::new(DEFAULT_PRE_COMMIT_MARGIN),
|
||||
post_commit_margin_decay: GeometricDecay::new(0.1, dev.min_post_commit_margin.get()),
|
||||
post_commit_margin: Cell::new(dev.min_post_commit_margin.get()),
|
||||
vblank_miss_sec: Cell::new(0),
|
||||
vblank_miss_this_sec: Default::default(),
|
||||
presentation_is_sync: Cell::new(false),
|
||||
presentation_is_zero_copy: Cell::new(false),
|
||||
});
|
||||
let futures = ConnectorFutures {
|
||||
_present: backend.state.eng.spawn2(
|
||||
"present loop",
|
||||
Phase::Present,
|
||||
slf.clone().present_loop(),
|
||||
),
|
||||
};
|
||||
Ok((slf, futures))
|
||||
}
|
||||
|
||||
pub(super) fn create_connector_display_data(
|
||||
connector: DrmConnector,
|
||||
dev: &Rc<MetalDrmDevice>,
|
||||
) -> Result<ConnectorDisplayData, DrmError> {
|
||||
let info = dev.master.get_connector_info(connector, true)?;
|
||||
let mut crtcs = BinarySearchMap::new();
|
||||
for encoder in info.encoders {
|
||||
if let Some(encoder) = dev.encoders.get(&encoder) {
|
||||
for (_, crtc) in &encoder.crtcs {
|
||||
crtcs.insert(crtc.id, crtc.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let props = collect_properties(&dev.master, connector)?;
|
||||
let connection = ConnectorStatus::from_drm(info.connection);
|
||||
let mut name = String::new();
|
||||
let mut manufacturer = String::new();
|
||||
let mut serial_number = String::new();
|
||||
let mut vrr_refresh_max_nsec = u64::MAX;
|
||||
let connector_id = ConnectorKernelId {
|
||||
ty: ConnectorType::from_drm(info.connector_type),
|
||||
idx: info.connector_type_id,
|
||||
};
|
||||
let mut supports_bt2020 = false;
|
||||
let mut supports_pq = false;
|
||||
let mut luminance = None;
|
||||
let mut primaries = Primaries::SRGB;
|
||||
'fetch_edid: {
|
||||
if connection != ConnectorStatus::Connected {
|
||||
break 'fetch_edid;
|
||||
}
|
||||
let edid = match props.get("EDID") {
|
||||
Ok(e) => e,
|
||||
_ => {
|
||||
log::warn!(
|
||||
"Connector {} is connected but has no EDID blob",
|
||||
connector_id,
|
||||
);
|
||||
break 'fetch_edid;
|
||||
}
|
||||
};
|
||||
let blob = match dev.master.getblob_vec::<u8>(DrmBlob(edid.value as _)) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not fetch edid property of connector {}: {}",
|
||||
connector_id,
|
||||
ErrorFmt(e)
|
||||
);
|
||||
break 'fetch_edid;
|
||||
}
|
||||
};
|
||||
let edid = match crate::edid::parse(&blob) {
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not parse edid property of connector {}: {}",
|
||||
connector_id,
|
||||
ErrorFmt(e)
|
||||
);
|
||||
break 'fetch_edid;
|
||||
}
|
||||
};
|
||||
manufacturer = edid.base_block.id_manufacturer_name.to_string();
|
||||
for descriptor in edid.base_block.descriptors.iter().flatten() {
|
||||
match descriptor {
|
||||
Descriptor::DisplayProductSerialNumber(s) => {
|
||||
serial_number.clone_from(s);
|
||||
}
|
||||
Descriptor::DisplayProductName(s) => {
|
||||
name.clone_from(s);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if name.is_empty() {
|
||||
log::warn!(
|
||||
"The display attached to connector {} does not have a product name descriptor",
|
||||
connector_id,
|
||||
);
|
||||
}
|
||||
if serial_number.is_empty() {
|
||||
log::warn!(
|
||||
"The display attached to connector {} does not have a serial number descriptor",
|
||||
connector_id,
|
||||
);
|
||||
serial_number = edid.base_block.id_serial_number.to_string();
|
||||
}
|
||||
let min_vrr_hz = 'fetch_min_hz: {
|
||||
for ext in &edid.extension_blocks {
|
||||
if let EdidExtension::CtaV3(cta) = ext {
|
||||
for data_block in &cta.data_blocks {
|
||||
if let CtaDataBlock::VendorAmd(amd) = data_block {
|
||||
break 'fetch_min_hz amd.minimum_refresh_hz as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for desc in &edid.base_block.descriptors {
|
||||
if let Some(desc) = desc
|
||||
&& let Descriptor::DisplayRangeLimitsAndAdditionalTiming(timings) = desc
|
||||
{
|
||||
break 'fetch_min_hz timings.vertical_field_rate_min as u64;
|
||||
}
|
||||
}
|
||||
0
|
||||
};
|
||||
if min_vrr_hz > 0 {
|
||||
vrr_refresh_max_nsec = 1_000_000_000 / min_vrr_hz;
|
||||
}
|
||||
let cc = &edid.base_block.chromaticity_coordinates;
|
||||
let map = |c: u16| F64(c as f64 / 1024.0);
|
||||
primaries = Primaries {
|
||||
r: (map(cc.red_x), map(cc.red_y)),
|
||||
g: (map(cc.green_x), map(cc.green_y)),
|
||||
b: (map(cc.blue_x), map(cc.blue_y)),
|
||||
wp: (map(cc.white_x), map(cc.white_y)),
|
||||
};
|
||||
for ext in &edid.extension_blocks {
|
||||
if let EdidExtension::CtaV3(cta) = ext {
|
||||
for data_block in &cta.data_blocks {
|
||||
match data_block {
|
||||
CtaDataBlock::Colorimetry(c) => {
|
||||
if c.bt2020_rgb {
|
||||
supports_bt2020 = true;
|
||||
}
|
||||
}
|
||||
CtaDataBlock::StaticHdrMetadata(h) => {
|
||||
if h.smpte_st_2084 {
|
||||
supports_pq = true;
|
||||
}
|
||||
if let Some(max) = h.max_luminance {
|
||||
luminance = Some(BackendLuminance {
|
||||
min: h.min_luminance.unwrap_or(0.0),
|
||||
max,
|
||||
max_fall: h.max_luminance.unwrap_or(max),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let output_id = OutputId::new(connector_id.to_string(), manufacturer, name, serial_number);
|
||||
let first_mode = info
|
||||
.modes
|
||||
.first()
|
||||
.cloned()
|
||||
.map(|m| m.to_backend())
|
||||
.unwrap_or_default();
|
||||
let persistent = match dev.backend.persistent_display_data.get(&output_id) {
|
||||
Some(ds) => {
|
||||
if connection != ConnectorStatus::Disconnected {
|
||||
log::info!("Reusing desired state for {:?}", output_id);
|
||||
}
|
||||
ds
|
||||
}
|
||||
None => {
|
||||
let ds = Rc::new(PersistentDisplayData {
|
||||
state: RefCell::new(BackendConnectorState {
|
||||
serial: dev.backend.state.backend_connector_state_serials.next(),
|
||||
enabled: true,
|
||||
active: true,
|
||||
mode: first_mode,
|
||||
non_desktop_override: None,
|
||||
vrr: false,
|
||||
tearing: false,
|
||||
format: XRGB8888,
|
||||
color_space: Default::default(),
|
||||
eotf: Default::default(),
|
||||
gamma_lut: Default::default(),
|
||||
}),
|
||||
});
|
||||
dev.backend
|
||||
.persistent_display_data
|
||||
.set(output_id.clone(), ds.clone());
|
||||
ds
|
||||
}
|
||||
};
|
||||
let mut desired_state = persistent.state.borrow_mut();
|
||||
if desired_state.mode == Mode::default() {
|
||||
desired_state.mode = first_mode;
|
||||
} else if info
|
||||
.modes
|
||||
.iter()
|
||||
.all(|m| m.to_backend() != desired_state.mode)
|
||||
{
|
||||
log::warn!("Discarding previously desired mode");
|
||||
desired_state.mode = first_mode;
|
||||
}
|
||||
let non_desktop = props.get("non-desktop")?.value != 0;
|
||||
let vrr_capable = match props.get("vrr_capable") {
|
||||
Ok(c) => c.value == 1,
|
||||
Err(_) => false,
|
||||
};
|
||||
if !vrr_capable && desired_state.vrr {
|
||||
log::warn!("Connector has lost VRR capability");
|
||||
desired_state.vrr = false;
|
||||
}
|
||||
{
|
||||
let viable = match desired_state.eotf {
|
||||
BackendEotfs::Default => true,
|
||||
BackendEotfs::Pq => supports_pq,
|
||||
};
|
||||
if !viable {
|
||||
log::warn!("Discarding previously desired EOTF");
|
||||
desired_state.eotf = BackendEotfs::Default;
|
||||
}
|
||||
}
|
||||
{
|
||||
let viable = match desired_state.color_space {
|
||||
BackendColorSpace::Default => true,
|
||||
BackendColorSpace::Bt2020 => supports_bt2020,
|
||||
};
|
||||
if !viable {
|
||||
log::warn!("Discarding previously desired color space");
|
||||
desired_state.color_space = BackendColorSpace::Default;
|
||||
}
|
||||
}
|
||||
drop(desired_state);
|
||||
let default_properties = create_default_properties(
|
||||
&props,
|
||||
&[
|
||||
("Broadcast RGB", DefaultValue::Enum("Automatic")),
|
||||
("HDR_SOURCE_METADATA", DefaultValue::Fixed(0)),
|
||||
("Output format", DefaultValue::Enum("Default")),
|
||||
("WRITEBACK_FB_ID", DefaultValue::Fixed(0)),
|
||||
("WRITEBACK_OUT_FENCE_PTR", DefaultValue::Fixed(0)),
|
||||
("content type", DefaultValue::Enum("No Data")),
|
||||
("dither", DefaultValue::Enum("off")),
|
||||
("max bpc", DefaultValue::RangeMax),
|
||||
],
|
||||
);
|
||||
let hdr_metadata_prop = props
|
||||
.get("HDR_OUTPUT_METADATA")
|
||||
.map(|p| p.map(|v| DrmBlob(v as _)))
|
||||
.ok();
|
||||
let mut hdr_metadata = None;
|
||||
let mut hdr_metadata_blob_id = DrmBlob::NONE;
|
||||
if let Some(p) = &hdr_metadata_prop {
|
||||
hdr_metadata_blob_id = p.value;
|
||||
hdr_metadata = Some(hdr_output_metadata::from_eotf(
|
||||
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
|
||||
));
|
||||
if p.value.is_some() {
|
||||
match dev.master.getblob::<hdr_output_metadata>(p.value) {
|
||||
Ok(m) => hdr_metadata = Some(m),
|
||||
_ => {
|
||||
log::debug!("Could not retrieve hdr output metadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let colorspace_prop = props.get("Colorspace").ok();
|
||||
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
|
||||
let drm_state = DrmConnectorState {
|
||||
crtc_id: crtc_id.value,
|
||||
color_space: colorspace_prop.map(|p| p.value),
|
||||
hdr_metadata,
|
||||
hdr_metadata_blob_id,
|
||||
hdr_metadata_blob: None,
|
||||
locked: true,
|
||||
fb: DrmFb::NONE,
|
||||
fb_idx: 0,
|
||||
cursor_fb: DrmFb::NONE,
|
||||
cursor_fb_idx: 0,
|
||||
cursor_x: 0,
|
||||
cursor_y: 0,
|
||||
out_fd: None,
|
||||
src_w: 0,
|
||||
src_h: 0,
|
||||
crtc_x: 0,
|
||||
crtc_y: 0,
|
||||
crtc_w: 0,
|
||||
crtc_h: 0,
|
||||
};
|
||||
Ok(ConnectorDisplayData {
|
||||
crtc_id: props.get("CRTC_ID")?.id,
|
||||
crtcs,
|
||||
first_mode,
|
||||
modes: info.modes,
|
||||
persistent,
|
||||
refresh: 0,
|
||||
non_desktop,
|
||||
non_desktop_effective: non_desktop,
|
||||
vrr_capable,
|
||||
_vrr_refresh_max_nsec: vrr_refresh_max_nsec,
|
||||
default_properties,
|
||||
untyped_properties: props.to_untyped(),
|
||||
connection,
|
||||
mm_width: info.mm_width,
|
||||
mm_height: info.mm_height,
|
||||
_subpixel: info.subpixel,
|
||||
supports_bt2020,
|
||||
supports_pq,
|
||||
primaries,
|
||||
luminance,
|
||||
connector_id,
|
||||
output_id,
|
||||
colorspace: colorspace_prop.map(|p| p.id),
|
||||
hdr_metadata: hdr_metadata_prop.map(|p| p.id),
|
||||
drm_state,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn create_encoder(
|
||||
encoder: DrmEncoder,
|
||||
master: &Rc<DrmMaster>,
|
||||
crtcs: &AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
||||
) -> Result<MetalEncoder, DrmError> {
|
||||
let info = master.get_encoder_info(encoder)?;
|
||||
let mut possible = AHashMap::new();
|
||||
for crtc in crtcs.values() {
|
||||
if info.possible_crtcs.contains(1 << crtc.idx) {
|
||||
possible.insert(crtc.id, crtc.clone());
|
||||
}
|
||||
}
|
||||
Ok(MetalEncoder {
|
||||
id: encoder,
|
||||
crtcs: possible,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn create_crtc(
|
||||
crtc: DrmCrtc,
|
||||
idx: usize,
|
||||
master: &Rc<DrmMaster>,
|
||||
planes: &AHashMap<DrmPlane, Rc<MetalPlane>>,
|
||||
) -> Result<MetalCrtc, DrmError> {
|
||||
let mask = 1 << idx;
|
||||
let mut possible_planes = BinarySearchMap::new();
|
||||
for plane in planes.values() {
|
||||
if plane.possible_crtcs.contains(mask) {
|
||||
possible_planes.insert(plane.id, plane.clone());
|
||||
}
|
||||
}
|
||||
let props = collect_properties(master, crtc)?;
|
||||
let default_properties = create_default_properties(
|
||||
&props,
|
||||
&[
|
||||
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
|
||||
("CTM", DefaultValue::Fixed(0)),
|
||||
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
|
||||
("OUT_FENCE_PTR", DefaultValue::Fixed(0)),
|
||||
],
|
||||
);
|
||||
let active = props.get("ACTIVE")?.map(|v| v == 1);
|
||||
let mode_id = props.get("MODE_ID")?.map(|v| DrmBlob(v as u32));
|
||||
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
|
||||
let out_fence_ptr = props.get("OUT_FENCE_PTR")?;
|
||||
let gamma_lut = props
|
||||
.get("GAMMA_LUT")
|
||||
.ok()
|
||||
.map(|v| v.map(|v| DrmBlob(v as u32)));
|
||||
let mut gamma_lut_size = None;
|
||||
if gamma_lut.is_some() {
|
||||
gamma_lut_size = props.get("GAMMA_LUT_SIZE").ok().map(|v| v.value as u32);
|
||||
}
|
||||
let mut mode = None;
|
||||
if mode_id.value.is_some() {
|
||||
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
|
||||
Ok(m) => mode = Some(m.into()),
|
||||
_ => {
|
||||
log::debug!("Could not retrieve current mode of connector");
|
||||
}
|
||||
}
|
||||
}
|
||||
let state = DrmCrtcState {
|
||||
active: active.value,
|
||||
mode,
|
||||
mode_blob_id: mode_id.value,
|
||||
mode_blob: None,
|
||||
vrr_enabled: vrr_enabled.value,
|
||||
assigned_connector: DrmConnector::NONE,
|
||||
gamma_lut: None,
|
||||
gamma_lut_blob_id: gamma_lut.map_or(DrmBlob::NONE, |v| v.value),
|
||||
gamma_lut_blob: None,
|
||||
};
|
||||
Ok(MetalCrtc {
|
||||
id: crtc,
|
||||
idx,
|
||||
master: master.clone(),
|
||||
default_properties,
|
||||
untyped_properties: RefCell::new(props.to_untyped()),
|
||||
lease: Cell::new(None),
|
||||
possible_planes,
|
||||
connector: Default::default(),
|
||||
pending_flip: Default::default(),
|
||||
drm_state: RefCell::new(state),
|
||||
active: active.id,
|
||||
mode_id: mode_id.id,
|
||||
vrr_enabled: vrr_enabled.id,
|
||||
out_fence_ptr: out_fence_ptr.id,
|
||||
gamma_lut: gamma_lut.map(|v| v.id),
|
||||
gamma_lut_size,
|
||||
sequence: Cell::new(0),
|
||||
have_queued_sequence: Cell::new(false),
|
||||
needs_vblank_emulation: Cell::new(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
|
||||
let info = master.get_plane_info(plane)?;
|
||||
let props = collect_properties(master, plane)?;
|
||||
let mut formats = AHashMap::new();
|
||||
if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) {
|
||||
for format in master.get_in_formats(*v as _)? {
|
||||
if format.modifiers.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some(f) = crate::format::formats().get(&format.format) {
|
||||
formats.insert(
|
||||
format.format,
|
||||
PlaneFormat {
|
||||
format: f,
|
||||
modifiers: format.modifiers,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for format in info.format_types {
|
||||
if let Some(f) = crate::format::formats().get(&format) {
|
||||
formats.insert(
|
||||
format,
|
||||
PlaneFormat {
|
||||
format: f,
|
||||
modifiers: indexset![INVALID_MODIFIER],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let ty = match props.props.get(b"type".as_bstr()) {
|
||||
Some((def, val)) => match &def.ty {
|
||||
DrmPropertyType::Enum { values, .. } => 'ty: {
|
||||
for v in values {
|
||||
if v.value == *val {
|
||||
match v.name.as_bytes() {
|
||||
b"Overlay" => break 'ty PlaneType::Overlay,
|
||||
b"Primary" => break 'ty PlaneType::Primary,
|
||||
b"Cursor" => break 'ty PlaneType::Cursor,
|
||||
_ => return Err(DrmError::UnknownPlaneType(v.name.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(DrmError::InvalidPlaneType(*val));
|
||||
}
|
||||
_ => return Err(DrmError::InvalidPlaneTypeProperty),
|
||||
},
|
||||
_ => {
|
||||
return Err(DrmError::MissingProperty(
|
||||
"type".to_string().into_boxed_str(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let default_properties = create_default_properties(
|
||||
&props,
|
||||
&[
|
||||
("AMD_PLANE_BLEND_LUT", DefaultValue::Fixed(0)),
|
||||
("AMD_PLANE_BLEND_TF", DefaultValue::Enum("Default")),
|
||||
("AMD_PLANE_CTM", DefaultValue::Fixed(0)),
|
||||
("AMD_PLANE_DEGAMMA_LUT", DefaultValue::Fixed(0)),
|
||||
("AMD_PLANE_HDR_MULT", DefaultValue::Fixed(0)),
|
||||
("AMD_PLANE_LUT3D", DefaultValue::Fixed(0)),
|
||||
("AMD_PLANE_SHAPER_LUT", DefaultValue::Fixed(0)),
|
||||
("AMD_PLANE_SHAPER_TF", DefaultValue::Enum("Default")),
|
||||
("alpha", DefaultValue::RangeMax),
|
||||
("pixel blend mode", DefaultValue::Enum("Pre-multiplied")),
|
||||
("rotation", DefaultValue::Bitmask(&["rotate-0"])),
|
||||
],
|
||||
);
|
||||
let fb_id = props.get("FB_ID")?.map(|v| DrmFb(v as _));
|
||||
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
|
||||
let crtc_x = props.get("CRTC_X")?.map(|v| v as i32);
|
||||
let crtc_y = props.get("CRTC_Y")?.map(|v| v as i32);
|
||||
let crtc_w = props.get("CRTC_W")?.map(|v| v as i32);
|
||||
let crtc_h = props.get("CRTC_H")?.map(|v| v as i32);
|
||||
let src_x = props.get("SRC_X")?.map(|v| v as u32);
|
||||
let src_y = props.get("SRC_Y")?.map(|v| v as u32);
|
||||
let src_w = props.get("SRC_W")?.map(|v| v as u32);
|
||||
let src_h = props.get("SRC_H")?.map(|v| v as u32);
|
||||
let in_fence_fd = props.get("IN_FENCE_FD")?;
|
||||
let state = DrmPlaneState {
|
||||
fb_id: fb_id.value,
|
||||
src_x: src_x.value,
|
||||
src_y: src_y.value,
|
||||
src_w: src_w.value,
|
||||
src_h: src_h.value,
|
||||
assigned_crtc: DrmCrtc::NONE,
|
||||
crtc_id: crtc_id.value,
|
||||
crtc_x: crtc_x.value,
|
||||
crtc_y: crtc_y.value,
|
||||
crtc_w: crtc_w.value,
|
||||
crtc_h: crtc_h.value,
|
||||
buffers: None,
|
||||
};
|
||||
Ok(MetalPlane {
|
||||
id: plane,
|
||||
master: master.clone(),
|
||||
default_properties,
|
||||
untyped_properties: RefCell::new(props.to_untyped()),
|
||||
ty,
|
||||
possible_crtcs: info.possible_crtcs,
|
||||
formats,
|
||||
drm_state: RefCell::new(state),
|
||||
fb_id: fb_id.id,
|
||||
crtc_id: crtc_id.id,
|
||||
crtc_x: crtc_x.id,
|
||||
crtc_y: crtc_y.id,
|
||||
crtc_w: crtc_w.id,
|
||||
crtc_h: crtc_h.id,
|
||||
src_x: src_x.id,
|
||||
src_y: src_y.id,
|
||||
src_w: src_w.id,
|
||||
src_h: src_h.id,
|
||||
in_fence_fd: in_fence_fd.id,
|
||||
mode_w: Cell::new(0),
|
||||
mode_h: Cell::new(0),
|
||||
lease: Cell::new(None),
|
||||
})
|
||||
}
|
||||
64
src/backends/metal/video/hardware_cursor.rs
Normal file
64
src/backends/metal/video/hardware_cursor.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use {
|
||||
super::MetalConnector,
|
||||
crate::{
|
||||
backend::{HardwareCursor, HardwareCursorUpdate},
|
||||
backends::metal::allocator::RenderBuffer,
|
||||
gfx_api::{FdSync, GfxFramebuffer},
|
||||
},
|
||||
std::{
|
||||
fmt::{Debug, Formatter},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct MetalHardwareCursor {
|
||||
pub connector: Rc<MetalConnector>,
|
||||
}
|
||||
|
||||
pub struct MetalHardwareCursorChange<'a> {
|
||||
pub cursor_swap_buffer: Option<Option<FdSync>>,
|
||||
pub cursor_enabled: bool,
|
||||
pub cursor_x: i32,
|
||||
pub cursor_y: i32,
|
||||
pub cursor_buffer: &'a RenderBuffer,
|
||||
pub cursor_size: (i32, i32),
|
||||
}
|
||||
|
||||
impl Debug for MetalHardwareCursor {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetalHardwareCursor")
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl HardwareCursor for MetalHardwareCursor {
|
||||
fn damage(&self) {
|
||||
self.connector.cursor_damage.set(true);
|
||||
if self.connector.buffers_idle.get() && self.connector.crtc_idle.get() {
|
||||
self.connector.schedule_present();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HardwareCursorUpdate for MetalHardwareCursorChange<'_> {
|
||||
fn set_enabled(&mut self, enabled: bool) {
|
||||
self.cursor_enabled = enabled;
|
||||
}
|
||||
|
||||
fn get_buffer(&self) -> Rc<dyn GfxFramebuffer> {
|
||||
self.cursor_buffer.render.fb.clone()
|
||||
}
|
||||
|
||||
fn set_position(&mut self, x: i32, y: i32) {
|
||||
self.cursor_x = x;
|
||||
self.cursor_y = y;
|
||||
}
|
||||
|
||||
fn swap_buffer(&mut self, sync: Option<FdSync>) {
|
||||
self.cursor_swap_buffer = Some(sync);
|
||||
}
|
||||
|
||||
fn size(&self) -> (i32, i32) {
|
||||
self.cursor_size
|
||||
}
|
||||
}
|
||||
84
src/backends/metal/video/lease.rs
Normal file
84
src/backends/metal/video/lease.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use {
|
||||
super::{FrontState, MetalConnector, MetalCrtc, MetalDrmDevice, MetalLeaseId, MetalPlane},
|
||||
crate::{
|
||||
backend::{BackendDrmLease, BackendDrmLessee, ConnectorEvent},
|
||||
utils::errorfmt::ErrorFmt,
|
||||
video::drm::DrmLease,
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct MetalLeaseData {
|
||||
pub lease: DrmLease,
|
||||
pub _lessee: Rc<dyn BackendDrmLessee>,
|
||||
pub connectors: Vec<Rc<MetalConnector>>,
|
||||
pub crtcs: Vec<Rc<MetalCrtc>>,
|
||||
pub planes: Vec<Rc<MetalPlane>>,
|
||||
pub revoked: Cell<bool>,
|
||||
}
|
||||
|
||||
impl MetalLeaseData {
|
||||
pub(super) fn try_revoke(&self) -> bool {
|
||||
if self.revoked.get() {
|
||||
return true;
|
||||
}
|
||||
let res = self.lease.try_revoke();
|
||||
if res {
|
||||
self.revoked.set(res);
|
||||
for c in &self.connectors {
|
||||
c.lease.take();
|
||||
if let Err(e) = c.update_properties() {
|
||||
log::error!("Could not update connector properties: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
for c in &self.crtcs {
|
||||
c.lease.take();
|
||||
if let Err(e) = c.update_properties() {
|
||||
log::error!("Could not update crtc properties: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
for p in &self.planes {
|
||||
p.lease.take();
|
||||
if let Err(e) = p.update_properties() {
|
||||
log::error!("Could not update plane properties: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MetalLease {
|
||||
pub(super) dev: Rc<MetalDrmDevice>,
|
||||
pub(super) id: MetalLeaseId,
|
||||
pub(super) fd: Rc<OwnedFd>,
|
||||
}
|
||||
|
||||
impl Drop for MetalLease {
|
||||
fn drop(&mut self) {
|
||||
if let Some(lease) = self.dev.leases.remove(&self.id) {
|
||||
if !self.dev.paused.get() {
|
||||
for c in &lease.connectors {
|
||||
match c.frontend_state.get() {
|
||||
FrontState::Removed
|
||||
| FrontState::Disconnected
|
||||
| FrontState::Connected { .. } => {}
|
||||
FrontState::Unavailable => {
|
||||
c.send_event(ConnectorEvent::Available);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !lease.try_revoke() {
|
||||
self.dev.leases_to_break.set(self.id, lease);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendDrmLease for MetalLease {
|
||||
fn fd(&self) -> &Rc<OwnedFd> {
|
||||
&self.fd
|
||||
}
|
||||
}
|
||||
364
src/backends/metal/video/model.rs
Normal file
364
src/backends/metal/video/model.rs
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
use {
|
||||
super::{copy_device::CopyDeviceHolder, lease::MetalLeaseData, properties::DefaultProperty},
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
backend::{
|
||||
BackendConnectorState, BackendLuminance, ConnectorEvent, ConnectorId,
|
||||
ConnectorKernelId, DrmDeviceId, DrmEvent, Mode, OutputId,
|
||||
},
|
||||
backends::metal::{
|
||||
MetalBackend,
|
||||
allocator::RenderBuffer,
|
||||
present::{DirectScanoutCache, PresentFb},
|
||||
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState},
|
||||
},
|
||||
cmm::{cmm_description::ColorDescription, cmm_primaries::Primaries},
|
||||
drm_feedback::DrmFeedback,
|
||||
format::Format,
|
||||
gfx_api::{FdSync, GfxContext},
|
||||
state::State,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, binary_search_map::BinarySearchMap, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, geometric_decay::GeometricDecay, numcell::NumCell,
|
||||
on_change::OnChange, opaque_cell::OpaqueCell,
|
||||
},
|
||||
video::{
|
||||
Modifier,
|
||||
dmabuf::DmaBufId,
|
||||
drm::{
|
||||
ConnectorStatus, DrmConnector, DrmCrtc, DrmEncoder, DrmMaster, DrmModeInfo,
|
||||
DrmObject, DrmPlane, DrmProperty,
|
||||
},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
indexmap::IndexSet,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
ffi::CString,
|
||||
fmt::{Debug, Formatter},
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub struct PendingDrmDevice {
|
||||
pub id: DrmDeviceId,
|
||||
pub devnum: c::dev_t,
|
||||
pub devnode: CString,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetalRenderContext {
|
||||
pub dev_id: DrmDeviceId,
|
||||
pub gfx: Rc<dyn GfxContext>,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
pub devnode: CString,
|
||||
pub copy_device: Rc<CopyDeviceHolder>,
|
||||
}
|
||||
|
||||
pub struct MetalDrmDevice {
|
||||
pub backend: Rc<MetalBackend>,
|
||||
pub id: DrmDeviceId,
|
||||
pub devnum: c::dev_t,
|
||||
pub devnode: CString,
|
||||
pub master: Rc<DrmMaster>,
|
||||
pub supports_kms: bool,
|
||||
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
||||
pub encoders: AHashMap<DrmEncoder, Rc<MetalEncoder>>,
|
||||
pub planes: AHashMap<DrmPlane, Rc<MetalPlane>>,
|
||||
pub cursor_width: u64,
|
||||
pub cursor_height: u64,
|
||||
pub supports_async_commit: bool,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
pub handle_events: HandleEvents,
|
||||
pub ctx: CloneCell<Rc<MetalRenderContext>>,
|
||||
pub copy_device: Rc<CopyDeviceHolder>,
|
||||
pub on_change: OnChange<DrmEvent>,
|
||||
pub direct_scanout_enabled: Cell<Option<bool>>,
|
||||
pub is_nvidia: bool,
|
||||
pub _is_amd: bool,
|
||||
pub lease_ids: MetalLeaseIds,
|
||||
pub leases: CopyHashMap<MetalLeaseId, MetalLeaseData>,
|
||||
pub leases_to_break: CopyHashMap<MetalLeaseId, MetalLeaseData>,
|
||||
pub paused: Cell<bool>,
|
||||
pub min_post_commit_margin: Cell<u64>,
|
||||
}
|
||||
|
||||
impl Debug for MetalDrmDevice {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetalDrmDevice").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl MetalDrmDevice {
|
||||
pub fn is_render_device(&self) -> bool {
|
||||
if let Some(ctx) = self.backend.ctx.get() {
|
||||
return ctx.dev_id == self.id;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HandleEvents {
|
||||
pub handle_events: Cell<Option<SpawnedFuture<()>>>,
|
||||
}
|
||||
|
||||
impl Debug for HandleEvents {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("HandleEvents").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetalDrmDeviceData {
|
||||
pub dev: Rc<MetalDrmDevice>,
|
||||
pub connectors: CopyHashMap<DrmConnector, Rc<MetalConnector>>,
|
||||
pub futures: CopyHashMap<DrmConnector, ConnectorFutures>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PersistentDisplayData {
|
||||
pub state: RefCell<BackendConnectorState>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectorDisplayData {
|
||||
pub crtc_id: DrmProperty,
|
||||
pub crtcs: BinarySearchMap<DrmCrtc, Rc<MetalCrtc>, 8>,
|
||||
pub first_mode: Mode,
|
||||
pub modes: Vec<DrmModeInfo>,
|
||||
pub persistent: Rc<PersistentDisplayData>,
|
||||
pub refresh: u32,
|
||||
pub non_desktop: bool,
|
||||
pub non_desktop_effective: bool,
|
||||
pub vrr_capable: bool,
|
||||
pub _vrr_refresh_max_nsec: u64,
|
||||
pub default_properties: Vec<DefaultProperty>,
|
||||
pub untyped_properties: AHashMap<DrmProperty, u64>,
|
||||
|
||||
pub connector_id: ConnectorKernelId,
|
||||
pub output_id: Rc<OutputId>,
|
||||
|
||||
pub connection: ConnectorStatus,
|
||||
pub mm_width: u32,
|
||||
pub mm_height: u32,
|
||||
pub _subpixel: u32,
|
||||
|
||||
pub supports_bt2020: bool,
|
||||
pub supports_pq: bool,
|
||||
pub primaries: Primaries,
|
||||
pub luminance: Option<BackendLuminance>,
|
||||
|
||||
pub colorspace: Option<DrmProperty>,
|
||||
pub hdr_metadata: Option<DrmProperty>,
|
||||
pub drm_state: DrmConnectorState,
|
||||
}
|
||||
|
||||
impl ConnectorDisplayData {
|
||||
fn update_refresh(&mut self, dev: &MetalDrmDevice) {
|
||||
self.refresh = 0;
|
||||
if self.drm_state.crtc_id.is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(crtc) = dev.crtcs.get(&self.drm_state.crtc_id) else {
|
||||
return;
|
||||
};
|
||||
let drm_state = &*crtc.drm_state.borrow();
|
||||
let Some(mode) = &drm_state.mode else {
|
||||
return;
|
||||
};
|
||||
let refresh_rate_mhz = mode.refresh_rate_millihz();
|
||||
if refresh_rate_mhz != 0 {
|
||||
self.refresh = (1_000_000_000_000u64 / refresh_rate_mhz as u64) as u32;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_non_desktop_effective(&mut self) {
|
||||
let state = &*self.persistent.state.borrow();
|
||||
self.non_desktop_effective =
|
||||
!state.enabled || state.non_desktop_override.unwrap_or(self.non_desktop);
|
||||
}
|
||||
|
||||
pub fn update_cached_fields(&mut self, dev: &MetalDrmDevice) {
|
||||
self.update_refresh(dev);
|
||||
self.update_non_desktop_effective();
|
||||
}
|
||||
}
|
||||
|
||||
linear_ids!(MetalLeaseIds, MetalLeaseId, u64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FrontState {
|
||||
Removed,
|
||||
Disconnected,
|
||||
Connected { non_desktop: bool },
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
pub struct MetalConnector {
|
||||
pub id: DrmConnector,
|
||||
pub kernel_id: Cell<ConnectorKernelId>,
|
||||
pub master: Rc<DrmMaster>,
|
||||
pub state: Rc<State>,
|
||||
|
||||
pub dev: Rc<MetalDrmDevice>,
|
||||
pub backend: Rc<MetalBackend>,
|
||||
|
||||
pub connector_id: ConnectorId,
|
||||
|
||||
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
||||
pub color_description: CloneCell<Rc<ColorDescription>>,
|
||||
|
||||
pub lease: Cell<Option<MetalLeaseId>>,
|
||||
|
||||
pub buffers_idle: Cell<bool>,
|
||||
pub crtc_idle: Cell<bool>,
|
||||
pub has_damage: NumCell<u64>,
|
||||
pub cursor_changed: Cell<bool>,
|
||||
pub cursor_damage: Cell<bool>,
|
||||
pub next_vblank_nsec: Cell<u64>,
|
||||
|
||||
pub display: RefCell<ConnectorDisplayData>,
|
||||
|
||||
pub frontend_state: Cell<FrontState>,
|
||||
|
||||
pub primary_plane: CloneCell<Option<Rc<MetalPlane>>>,
|
||||
pub cursor_plane: CloneCell<Option<Rc<MetalPlane>>>,
|
||||
|
||||
pub crtc: CloneCell<Option<Rc<MetalCrtc>>>,
|
||||
|
||||
pub on_change: OnChange<ConnectorEvent>,
|
||||
|
||||
pub present_trigger: AsyncEvent,
|
||||
|
||||
pub cursor_x: Cell<i32>,
|
||||
pub cursor_y: Cell<i32>,
|
||||
pub cursor_enabled: Cell<bool>,
|
||||
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
||||
pub cursor_swap_buffer: Cell<bool>,
|
||||
pub cursor_sync: CloneCell<Option<FdSync>>,
|
||||
|
||||
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
|
||||
pub active_framebuffer: RefCell<Option<PresentFb>>,
|
||||
pub next_framebuffer: OpaqueCell<Option<PresentFb>>,
|
||||
pub direct_scanout_active: Cell<bool>,
|
||||
|
||||
pub version: NumCell<u64>,
|
||||
pub expected_sequence: Cell<Option<u64>>,
|
||||
pub pre_commit_margin: Cell<u64>,
|
||||
pub pre_commit_margin_decay: GeometricDecay,
|
||||
pub post_commit_margin: Cell<u64>,
|
||||
pub post_commit_margin_decay: GeometricDecay,
|
||||
pub vblank_miss_sec: Cell<u32>,
|
||||
pub vblank_miss_this_sec: NumCell<u32>,
|
||||
pub presentation_is_sync: Cell<bool>,
|
||||
pub presentation_is_zero_copy: Cell<bool>,
|
||||
}
|
||||
|
||||
impl Debug for MetalConnector {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetalConnnector").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConnectorFutures {
|
||||
pub _present: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
impl Debug for ConnectorFutures {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ConnectorFutures").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MetalCrtc {
|
||||
pub id: DrmCrtc,
|
||||
pub idx: usize,
|
||||
pub master: Rc<DrmMaster>,
|
||||
pub default_properties: Vec<DefaultProperty>,
|
||||
pub untyped_properties: RefCell<AHashMap<DrmProperty, u64>>,
|
||||
|
||||
pub lease: Cell<Option<MetalLeaseId>>,
|
||||
|
||||
pub possible_planes: BinarySearchMap<DrmPlane, Rc<MetalPlane>, 8>,
|
||||
|
||||
pub connector: CloneCell<Option<Rc<MetalConnector>>>,
|
||||
pub pending_flip: CloneCell<Option<Rc<MetalConnector>>>,
|
||||
|
||||
pub active: DrmProperty,
|
||||
pub mode_id: DrmProperty,
|
||||
pub vrr_enabled: DrmProperty,
|
||||
pub out_fence_ptr: DrmProperty,
|
||||
pub gamma_lut: Option<DrmProperty>,
|
||||
pub gamma_lut_size: Option<u32>,
|
||||
pub drm_state: RefCell<DrmCrtcState>,
|
||||
|
||||
pub sequence: Cell<u64>,
|
||||
pub have_queued_sequence: Cell<bool>,
|
||||
pub needs_vblank_emulation: Cell<bool>,
|
||||
}
|
||||
|
||||
impl Debug for MetalCrtc {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetalCrtc").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetalEncoder {
|
||||
pub id: DrmEncoder,
|
||||
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum PlaneType {
|
||||
Overlay,
|
||||
Primary,
|
||||
Cursor,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlaneFormat {
|
||||
pub format: &'static Format,
|
||||
pub modifiers: IndexSet<Modifier>,
|
||||
}
|
||||
|
||||
pub struct MetalPlane {
|
||||
pub id: DrmPlane,
|
||||
pub master: Rc<DrmMaster>,
|
||||
pub default_properties: Vec<DefaultProperty>,
|
||||
pub untyped_properties: RefCell<AHashMap<DrmProperty, u64>>,
|
||||
|
||||
pub ty: PlaneType,
|
||||
|
||||
pub possible_crtcs: u32,
|
||||
pub formats: AHashMap<u32, PlaneFormat>,
|
||||
|
||||
pub lease: Cell<Option<MetalLeaseId>>,
|
||||
|
||||
pub mode_w: Cell<i32>,
|
||||
pub mode_h: Cell<i32>,
|
||||
|
||||
pub crtc_id: DrmProperty,
|
||||
pub crtc_x: DrmProperty,
|
||||
pub crtc_y: DrmProperty,
|
||||
pub crtc_w: DrmProperty,
|
||||
pub crtc_h: DrmProperty,
|
||||
pub src_x: DrmProperty,
|
||||
pub src_y: DrmProperty,
|
||||
pub src_w: DrmProperty,
|
||||
pub src_h: DrmProperty,
|
||||
pub in_fence_fd: DrmProperty,
|
||||
pub fb_id: DrmProperty,
|
||||
|
||||
pub drm_state: RefCell<DrmPlaneState>,
|
||||
}
|
||||
|
||||
impl Debug for MetalPlane {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetalPlane").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
144
src/backends/metal/video/properties.rs
Normal file
144
src/backends/metal/video/properties.rs
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
use {
|
||||
crate::video::drm::{
|
||||
DrmError, DrmMaster, DrmObject, DrmProperty, DrmPropertyDefinition, DrmPropertyType,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
bstr::{BString, ByteSlice},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultProperty {
|
||||
pub name: &'static str,
|
||||
pub prop: DrmProperty,
|
||||
pub value: u64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(super) enum DefaultValue {
|
||||
Fixed(u64),
|
||||
Enum(&'static str),
|
||||
Bitmask(&'static [&'static str]),
|
||||
RangeMax,
|
||||
}
|
||||
|
||||
pub(super) fn create_default_properties(
|
||||
props: &CollectedProperties,
|
||||
defaults: &[(&'static str, DefaultValue)],
|
||||
) -> Vec<DefaultProperty> {
|
||||
let mut res = vec![];
|
||||
let mut defaults = defaults.iter();
|
||||
'outer: loop {
|
||||
let Some(&(name, def)) = defaults.next() else {
|
||||
break;
|
||||
};
|
||||
if let Some((definition, _)) = props.props.get(name.as_bytes().as_bstr()) {
|
||||
let value = match def {
|
||||
DefaultValue::Fixed(v) => v,
|
||||
DefaultValue::Enum(e) => match &definition.ty {
|
||||
DrmPropertyType::Enum {
|
||||
values,
|
||||
bitmask: false,
|
||||
} => match values.iter().find(|v| v.name == e) {
|
||||
None => continue,
|
||||
Some(v) => v.value,
|
||||
},
|
||||
_ => continue,
|
||||
},
|
||||
DefaultValue::Bitmask(e) => match &definition.ty {
|
||||
DrmPropertyType::Enum {
|
||||
values,
|
||||
bitmask: true,
|
||||
} => {
|
||||
let mut res = 0;
|
||||
for &e in e {
|
||||
match values.iter().find(|v| v.name == e) {
|
||||
None => continue 'outer,
|
||||
Some(v) => res |= 1 << v.value,
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
DefaultValue::RangeMax => match &definition.ty {
|
||||
DrmPropertyType::Range { max, .. } => *max,
|
||||
DrmPropertyType::SignedRange { max, .. } => *max as u64,
|
||||
_ => continue,
|
||||
},
|
||||
};
|
||||
res.push(DefaultProperty {
|
||||
name,
|
||||
prop: definition.id,
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub(super) fn collect_properties<T: DrmObject>(
|
||||
master: &Rc<DrmMaster>,
|
||||
t: T,
|
||||
) -> Result<CollectedProperties, DrmError> {
|
||||
let mut props = AHashMap::new();
|
||||
for prop in master.get_properties(t)? {
|
||||
let def = master.get_property(prop.id)?;
|
||||
props.insert(def.name.clone(), (def, prop.value));
|
||||
}
|
||||
Ok(CollectedProperties { props })
|
||||
}
|
||||
|
||||
pub(super) fn collect_untyped_properties<T: DrmObject>(
|
||||
master: &Rc<DrmMaster>,
|
||||
t: T,
|
||||
props: &mut AHashMap<DrmProperty, u64>,
|
||||
) -> Result<(), DrmError> {
|
||||
props.clear();
|
||||
for prop in master.get_properties(t)? {
|
||||
props.insert(prop.id, prop.value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) struct CollectedProperties {
|
||||
pub(super) props: AHashMap<BString, (DrmPropertyDefinition, u64)>,
|
||||
}
|
||||
|
||||
impl CollectedProperties {
|
||||
pub(super) fn get(&self, name: &str) -> Result<TypedProperty<u64>, DrmError> {
|
||||
match self.props.get(name.as_bytes().as_bstr()) {
|
||||
Some((def, value)) => Ok(TypedProperty {
|
||||
id: def.id,
|
||||
value: *value,
|
||||
}),
|
||||
_ => Err(DrmError::MissingProperty(name.to_string().into_boxed_str())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_untyped(&self) -> AHashMap<DrmProperty, u64> {
|
||||
let mut res = AHashMap::new();
|
||||
for (def, val) in self.props.values() {
|
||||
res.insert(def.id, *val);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TypedProperty<T> {
|
||||
pub id: DrmProperty,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
impl<T: Copy> TypedProperty<T> {
|
||||
pub(super) fn map<U, F>(self, f: F) -> TypedProperty<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
TypedProperty {
|
||||
id: self.id,
|
||||
value: f(self.value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ use {
|
|||
ButtonState, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
|
||||
DrmEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceClickMethod, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo,
|
||||
ScrollAxis, TransformMatrix,
|
||||
OutputId, ScrollAxis, TransformMatrix,
|
||||
transaction::{
|
||||
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
||||
BackendConnectorTransactionError, BackendConnectorTransactionType,
|
||||
|
|
@ -20,7 +20,6 @@ use {
|
|||
gfx_api::{
|
||||
AcquireSync, GfxApi, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||
},
|
||||
ifs::wl_output::OutputId,
|
||||
state::State,
|
||||
time::Time,
|
||||
utils::{
|
||||
|
|
|
|||
39
src/bugs.rs
39
src/bugs.rs
|
|
@ -1,38 +1 @@
|
|||
use {ahash::AHashMap, std::sync::LazyLock};
|
||||
|
||||
static BUGS: LazyLock<AHashMap<&'static str, Bugs>> = LazyLock::new(|| {
|
||||
let mut map = AHashMap::new();
|
||||
map.insert(
|
||||
"chromium",
|
||||
Bugs {
|
||||
respect_min_max_size: true,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
map.insert(
|
||||
"Alacritty",
|
||||
Bugs {
|
||||
min_width: Some(100),
|
||||
min_height: Some(100),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
map
|
||||
});
|
||||
|
||||
pub fn get(app_id: &str) -> &'static Bugs {
|
||||
BUGS.get(app_id).unwrap_or(&NONE)
|
||||
}
|
||||
|
||||
pub static NONE: Bugs = Bugs {
|
||||
respect_min_max_size: false,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Bugs {
|
||||
pub respect_min_max_size: bool,
|
||||
pub min_width: Option<i32>,
|
||||
pub min_height: Option<i32>,
|
||||
}
|
||||
pub use jay_bugs::*;
|
||||
|
|
|
|||
50
src/cli.rs
50
src/cli.rs
|
|
@ -3,6 +3,7 @@ mod color;
|
|||
mod color_management;
|
||||
mod config;
|
||||
mod damage_tracking;
|
||||
mod dpms;
|
||||
mod duration;
|
||||
mod generate;
|
||||
mod idle;
|
||||
|
|
@ -13,8 +14,6 @@ mod pid;
|
|||
mod quit;
|
||||
mod randr;
|
||||
mod reexec;
|
||||
mod run_privileged;
|
||||
mod run_tagged;
|
||||
pub mod screenshot;
|
||||
mod seat_test;
|
||||
mod set_log_level;
|
||||
|
|
@ -28,15 +27,14 @@ use {
|
|||
cli::{
|
||||
clients::ClientsArgs, color_management::ColorManagementArgs, config::ConfigArgs,
|
||||
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs,
|
||||
json::VERBOSE_JSON, randr::RandrArgs, reexec::ReexecArgs, run_tagged::RunTaggedArgs,
|
||||
tree::TreeArgs, xwayland::XwaylandArgs,
|
||||
json::VERBOSE_JSON, randr::RandrArgs, reexec::ReexecArgs, tree::TreeArgs,
|
||||
xwayland::XwaylandArgs,
|
||||
},
|
||||
compositor::{LogLevel, start_compositor},
|
||||
format::{Format, ref_formats},
|
||||
portal,
|
||||
compositor::start_compositor,
|
||||
logger::LogLevel,
|
||||
pr_caps::drop_all_pr_caps,
|
||||
},
|
||||
clap::{Args, Parser, Subcommand, ValueEnum, ValueHint, builder::PossibleValue},
|
||||
clap::{Args, Parser, Subcommand, ValueEnum, ValueHint},
|
||||
clap_complete::Shell,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
};
|
||||
|
|
@ -85,14 +83,10 @@ pub enum Cmd {
|
|||
Screenshot(ScreenshotArgs),
|
||||
/// Inspect/modify the idle (screensaver) settings.
|
||||
Idle(IdleArgs),
|
||||
/// Run a privileged program.
|
||||
RunPrivileged(RunPrivilegedArgs),
|
||||
/// Run a program with a connection tag.
|
||||
RunTagged(RunTaggedArgs),
|
||||
/// Turn monitors on or off.
|
||||
Dpms(DpmsArgs),
|
||||
/// Tests the events produced by a seat.
|
||||
SeatTest(SeatTestArgs),
|
||||
/// Run the desktop portal.
|
||||
Portal,
|
||||
/// Inspect/modify graphics card and connector settings.
|
||||
Randr(RandrArgs),
|
||||
/// Inspect/modify input settings.
|
||||
|
|
@ -132,10 +126,16 @@ pub struct IdleArgs {
|
|||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct RunPrivilegedArgs {
|
||||
/// The program to run
|
||||
#[clap(required = true, trailing_var_arg = true, value_hint = ValueHint::CommandWithArguments)]
|
||||
pub program: Vec<String>,
|
||||
pub struct DpmsArgs {
|
||||
/// Whether monitors should be on or off.
|
||||
#[clap(value_enum)]
|
||||
pub state: DpmsState,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum DpmsState {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Hash, Default, PartialEq)]
|
||||
|
|
@ -224,16 +224,6 @@ pub struct GenerateArgs {
|
|||
shell: Shell,
|
||||
}
|
||||
|
||||
impl ValueEnum for &'static Format {
|
||||
fn value_variants<'a>() -> &'a [Self] {
|
||||
ref_formats()
|
||||
}
|
||||
|
||||
fn to_possible_value(&self) -> Option<PossibleValue> {
|
||||
Some(PossibleValue::new(self.name))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let cli = Jay::parse();
|
||||
if not_matches!(cli.command, Cmd::Run(_)) {
|
||||
|
|
@ -250,11 +240,9 @@ pub fn main() {
|
|||
Cmd::SetLogLevel(a) => set_log_level::main(cli.global, a),
|
||||
Cmd::Screenshot(a) => screenshot::main(cli.global, a),
|
||||
Cmd::Idle(a) => idle::main(cli.global, a),
|
||||
Cmd::Dpms(a) => dpms::main(cli.global, a),
|
||||
Cmd::Unlock => unlock::main(cli.global),
|
||||
Cmd::RunPrivileged(a) => run_privileged::main(cli.global, a),
|
||||
Cmd::RunTagged(a) => run_tagged::main(cli.global, a),
|
||||
Cmd::SeatTest(a) => seat_test::main(cli.global, a),
|
||||
Cmd::Portal => portal::run_freestanding(cli.global),
|
||||
Cmd::Randr(a) => randr::main(cli.global, a),
|
||||
Cmd::Input(a) => input::main(cli.global, a),
|
||||
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@ pub struct Client {
|
|||
pub is_xwayland: bool,
|
||||
pub comm: Option<String>,
|
||||
pub exe: Option<String>,
|
||||
pub tag: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn handle_client_query(
|
||||
|
|
@ -212,9 +211,6 @@ pub async fn handle_client_query(
|
|||
Exe::handle(tl, id, c.clone(), |c, event| {
|
||||
last!(c).exe = Some(event.exe.to_string());
|
||||
});
|
||||
Tag::handle(tl, id, c.clone(), |c, event| {
|
||||
last!(c).tag = Some(event.tag.to_string());
|
||||
});
|
||||
tl.round_trip().await;
|
||||
mem::take(&mut *c.borrow_mut())
|
||||
.into_iter()
|
||||
|
|
@ -253,7 +249,6 @@ impl ClientPrinter<'_> {
|
|||
bol!(is_xwayland, "xwayland");
|
||||
opt!(comm, "comm");
|
||||
opt!(exe, "exe");
|
||||
opt!(tag, "tag");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,6 +264,5 @@ pub fn make_json_client(client: &Client) -> JsonClient<'_> {
|
|||
is_xwayland: client.is_xwayland,
|
||||
comm: client.comm.as_deref(),
|
||||
exe: client.exe.as_deref(),
|
||||
tag: client.tag.as_deref(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
src/cli/dpms.rs
Normal file
23
src/cli/dpms.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{DpmsArgs, DpmsState, GlobalArgs},
|
||||
tools::tool_client::{ToolClient, with_tool_client},
|
||||
wire::jay_compositor::SetDpms,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub fn main(global: GlobalArgs, args: DpmsArgs) {
|
||||
with_tool_client(global.log_level, |tc| async move {
|
||||
run(tc, args).await;
|
||||
});
|
||||
}
|
||||
|
||||
async fn run(tc: Rc<ToolClient>, args: DpmsArgs) {
|
||||
let comp = tc.jay_compositor().await;
|
||||
tc.send(SetDpms {
|
||||
self_id: comp,
|
||||
active: (args.state == DpmsState::On) as u32,
|
||||
});
|
||||
tc.round_trip().await;
|
||||
}
|
||||
|
|
@ -66,8 +66,6 @@ pub struct JsonClient<'a> {
|
|||
pub comm: Option<&'a str>,
|
||||
#[serde(skip_serializing_if = "is_none")]
|
||||
pub exe: Option<&'a str>,
|
||||
#[serde(skip_serializing_if = "is_none")]
|
||||
pub tag: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{GlobalArgs, RunPrivilegedArgs},
|
||||
compositor::WAYLAND_DISPLAY,
|
||||
logger::Logger,
|
||||
utils::{errorfmt::ErrorFmt, oserror::OsErrorExt, xrd::xrd},
|
||||
},
|
||||
std::path::PathBuf,
|
||||
uapi::UstrPtr,
|
||||
};
|
||||
|
||||
pub fn main(global: GlobalArgs, args: RunPrivilegedArgs) {
|
||||
Logger::install_stderr(global.log_level);
|
||||
if let Some(xrd) = xrd() {
|
||||
let mut wd = match std::env::var(WAYLAND_DISPLAY) {
|
||||
Ok(v) => v,
|
||||
_ => fatal!("{} is not set", WAYLAND_DISPLAY),
|
||||
};
|
||||
wd.push_str(".jay");
|
||||
let mut path = PathBuf::from(xrd);
|
||||
path.push(&wd);
|
||||
if path.exists() {
|
||||
unsafe {
|
||||
std::env::set_var(WAYLAND_DISPLAY, &wd);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut argv = UstrPtr::new();
|
||||
for arg in &args.program {
|
||||
argv.push(arg.as_str());
|
||||
}
|
||||
let program = args.program[0].as_str();
|
||||
let res = uapi::execvp(program, &argv).to_os_error().unwrap_err();
|
||||
fatal!("Could not execute `{}`: {}", program, ErrorFmt(res));
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::GlobalArgs,
|
||||
compositor::WAYLAND_DISPLAY,
|
||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||
utils::{errorfmt::ErrorFmt, oserror::OsErrorExt},
|
||||
wire::{jay_acceptor_request, jay_compositor},
|
||||
},
|
||||
clap::{Args, ValueHint},
|
||||
std::{cell::Cell, env, rc::Rc},
|
||||
uapi::UstrPtr,
|
||||
};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct RunTaggedArgs {
|
||||
/// Specifies a tag to apply to all spawned wayland connections.
|
||||
tag: String,
|
||||
/// The program to run.
|
||||
#[clap(required = true, trailing_var_arg = true, value_hint = ValueHint::CommandWithArguments)]
|
||||
pub program: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn main(global: GlobalArgs, run_tagged_args: RunTaggedArgs) {
|
||||
with_tool_client(global.log_level, |tc| async move {
|
||||
let run_tagged = Rc::new(RunTagged { tc: tc.clone() });
|
||||
run_tagged.run(run_tagged_args).await;
|
||||
});
|
||||
}
|
||||
|
||||
struct RunTagged {
|
||||
tc: Rc<ToolClient>,
|
||||
}
|
||||
|
||||
impl RunTagged {
|
||||
async fn run(&self, args: RunTaggedArgs) {
|
||||
let tc = &self.tc;
|
||||
let comp = tc.jay_compositor().await;
|
||||
let req = tc.id();
|
||||
tc.send(jay_compositor::GetTaggedAcceptor {
|
||||
self_id: comp,
|
||||
id: req,
|
||||
tag: &args.tag,
|
||||
});
|
||||
let res = Rc::new(Cell::new(None));
|
||||
jay_acceptor_request::Done::handle(&tc, req, res.clone(), |res, ev| {
|
||||
res.set(Some(Ok(ev.name.to_owned())));
|
||||
});
|
||||
jay_acceptor_request::Failed::handle(&tc, req, res.clone(), |res, ev| {
|
||||
res.set(Some(Err(ev.msg.to_owned())));
|
||||
});
|
||||
tc.round_trip().await;
|
||||
match res.take().unwrap() {
|
||||
Ok(n) => {
|
||||
unsafe {
|
||||
env::set_var(WAYLAND_DISPLAY, &n);
|
||||
}
|
||||
let mut argv = UstrPtr::new();
|
||||
for arg in &args.program {
|
||||
argv.push(arg.as_str());
|
||||
}
|
||||
let program = args.program[0].as_str();
|
||||
let res = uapi::execvp(program, &argv).to_os_error().unwrap_err();
|
||||
fatal!("Could not execute `{}`: {}", program, ErrorFmt(res));
|
||||
}
|
||||
Err(msg) => {
|
||||
fatal!("Could not create acceptor: {}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
111
src/client.rs
111
src/client.rs
|
|
@ -14,7 +14,6 @@ use {
|
|||
},
|
||||
leaks::Tracker,
|
||||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||
security_context_acceptor::AcceptorMetadata,
|
||||
state::State,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -25,7 +24,6 @@ use {
|
|||
pending_serial::PendingSerial,
|
||||
pid_info::{PidInfo, get_pid_info, get_socket_creds},
|
||||
pidfd_send_signal::pidfd_send_signal,
|
||||
static_text::StaticText,
|
||||
},
|
||||
wire::WlRegistryId,
|
||||
},
|
||||
|
|
@ -43,63 +41,14 @@ use {
|
|||
};
|
||||
pub use {
|
||||
error::{ClientError, ParserError},
|
||||
objects::MIN_SERVER_ID,
|
||||
};
|
||||
#[cfg(feature = "it")]
|
||||
pub use objects::MIN_SERVER_ID;
|
||||
|
||||
mod error;
|
||||
mod objects;
|
||||
mod tasks;
|
||||
|
||||
bitflags! {
|
||||
ClientCaps: u32;
|
||||
CAP_DATA_CONTROL_MANAGER = 1 << 0,
|
||||
CAP_VIRTUAL_KEYBOARD_MANAGER = 1 << 1,
|
||||
CAP_FOREIGN_TOPLEVEL_LIST = 1 << 2,
|
||||
CAP_IDLE_NOTIFIER = 1 << 3,
|
||||
CAP_SESSION_LOCK_MANAGER = 1 << 4,
|
||||
CAP_JAY_COMPOSITOR = 1 << 5,
|
||||
CAP_LAYER_SHELL = 1 << 6,
|
||||
CAP_SCREENCOPY_MANAGER = 1 << 7,
|
||||
CAP_SEAT_MANAGER = 1 << 8,
|
||||
CAP_DRM_LEASE = 1 << 9,
|
||||
CAP_INPUT_METHOD = 1 << 10,
|
||||
CAP_WORKSPACE = 1 << 11,
|
||||
CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12,
|
||||
CAP_HEAD_MANAGER = 1 << 13,
|
||||
CAP_GAMMA_CONTROL_MANAGER = 1 << 14,
|
||||
CAP_VIRTUAL_POINTER_MANAGER = 1 << 15,
|
||||
CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING = 1 << 16,
|
||||
}
|
||||
|
||||
impl StaticText for ClientCapsEnum {
|
||||
fn text(&self) -> &'static str {
|
||||
match self {
|
||||
ClientCapsEnum::CAP_DATA_CONTROL_MANAGER => "data-control",
|
||||
ClientCapsEnum::CAP_VIRTUAL_KEYBOARD_MANAGER => "virtual-keyboard",
|
||||
ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_LIST => "foreign-toplevel-list",
|
||||
ClientCapsEnum::CAP_IDLE_NOTIFIER => "idle-notifier",
|
||||
ClientCapsEnum::CAP_SESSION_LOCK_MANAGER => "session-lock",
|
||||
ClientCapsEnum::CAP_JAY_COMPOSITOR => "jay-compositor",
|
||||
ClientCapsEnum::CAP_LAYER_SHELL => "layer-shell",
|
||||
ClientCapsEnum::CAP_SCREENCOPY_MANAGER => "screencopy",
|
||||
ClientCapsEnum::CAP_SEAT_MANAGER => "seat-manager",
|
||||
ClientCapsEnum::CAP_DRM_LEASE => "drm-lease",
|
||||
ClientCapsEnum::CAP_INPUT_METHOD => "input-method",
|
||||
ClientCapsEnum::CAP_WORKSPACE => "workspace-manager",
|
||||
ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_MANAGER => "foreign-toplevel-manager",
|
||||
ClientCapsEnum::CAP_HEAD_MANAGER => "head-manager",
|
||||
ClientCapsEnum::CAP_GAMMA_CONTROL_MANAGER => "gamma-control-manager",
|
||||
ClientCapsEnum::CAP_VIRTUAL_POINTER_MANAGER => "virtual-pointer",
|
||||
ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING => {
|
||||
"foreign-toplevel-geometry-tracking"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0);
|
||||
pub const CAPS_DEFAULT_SANDBOXED: ClientCaps = ClientCaps(CAP_DRM_LEASE.0);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct ClientId(u64);
|
||||
|
||||
|
|
@ -125,6 +74,15 @@ pub struct Clients {
|
|||
shutdown_clients: RefCell<AHashMap<ClientId, ClientHolder>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClientMetadata {
|
||||
pub sandboxed: bool,
|
||||
pub sandbox_engine: Option<String>,
|
||||
pub app_id: Option<String>,
|
||||
pub instance_id: Option<String>,
|
||||
pub tag: Option<String>,
|
||||
}
|
||||
|
||||
impl Clients {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
@ -156,24 +114,12 @@ impl Clients {
|
|||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: Rc<OwnedFd>,
|
||||
bounding_caps: ClientCaps,
|
||||
set_bounding_caps_for_children: bool,
|
||||
acceptor: &Rc<AcceptorMetadata>,
|
||||
metadata: &Rc<ClientMetadata>,
|
||||
) -> Result<(), ClientError> {
|
||||
let Some((uid, pid)) = get_socket_creds(&socket) else {
|
||||
return Ok(());
|
||||
};
|
||||
self.spawn2(
|
||||
id,
|
||||
global,
|
||||
socket,
|
||||
uid,
|
||||
pid,
|
||||
bounding_caps,
|
||||
set_bounding_caps_for_children,
|
||||
false,
|
||||
acceptor,
|
||||
)?;
|
||||
self.spawn2(id, global, socket, uid, pid, false, metadata)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -184,15 +130,9 @@ impl Clients {
|
|||
socket: Rc<OwnedFd>,
|
||||
uid: c::uid_t,
|
||||
pid: c::pid_t,
|
||||
bounding_caps: ClientCaps,
|
||||
set_bounding_caps_for_children: bool,
|
||||
is_xwayland: bool,
|
||||
acceptor: &Rc<AcceptorMetadata>,
|
||||
metadata: &Rc<ClientMetadata>,
|
||||
) -> Result<Rc<Client>, ClientError> {
|
||||
let effective_caps = match acceptor.sandboxed {
|
||||
true => CAPS_DEFAULT_SANDBOXED,
|
||||
false => CAPS_DEFAULT,
|
||||
};
|
||||
let data = Rc::new_cyclic(|slf| Client {
|
||||
id,
|
||||
state: global.clone(),
|
||||
|
|
@ -204,8 +144,6 @@ impl Clients {
|
|||
shutdown: Default::default(),
|
||||
tracker: Default::default(),
|
||||
is_xwayland,
|
||||
effective_caps: Cell::new(effective_caps & bounding_caps),
|
||||
bounding_caps_for_children: Cell::new(bounding_caps),
|
||||
last_enter_serial: Default::default(),
|
||||
pid_info: get_pid_info(uid, pid),
|
||||
serials: Default::default(),
|
||||
|
|
@ -223,13 +161,9 @@ impl Clients {
|
|||
focus_stealing_serial: Default::default(),
|
||||
changed_properties: Default::default(),
|
||||
destroyed: Default::default(),
|
||||
acceptor: acceptor.clone(),
|
||||
metadata: metadata.clone(),
|
||||
});
|
||||
track!(data, data);
|
||||
global.update_capabilities(&data, bounding_caps, set_bounding_caps_for_children);
|
||||
if acceptor.secure || is_xwayland {
|
||||
data.effective_caps.set(ClientCaps::all());
|
||||
}
|
||||
let display = Rc::new(WlDisplay::new(&data));
|
||||
track!(data, display);
|
||||
data.objects.display.set(Some(display.clone()));
|
||||
|
|
@ -239,13 +173,12 @@ impl Clients {
|
|||
data: data.clone(),
|
||||
};
|
||||
log::info!(
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}, caps: {:?}",
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}",
|
||||
id,
|
||||
pid,
|
||||
uid,
|
||||
client.data.socket.raw(),
|
||||
data.pid_info.comm,
|
||||
data.effective_caps.get(),
|
||||
);
|
||||
client.data.property_changed(CL_CHANGED_NEW);
|
||||
self.clients.borrow_mut().insert(client.data.id, client);
|
||||
|
|
@ -268,15 +201,13 @@ impl Clients {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn broadcast<B>(&self, required_caps: ClientCaps, xwayland_only: bool, mut f: B)
|
||||
pub fn broadcast<B>(&self, xwayland_only: bool, mut f: B)
|
||||
where
|
||||
B: FnMut(&Rc<Client>),
|
||||
{
|
||||
let clients = self.clients.borrow();
|
||||
for client in clients.values() {
|
||||
if client.data.effective_caps.get().contains(required_caps)
|
||||
&& (!xwayland_only || client.data.is_xwayland)
|
||||
{
|
||||
if !xwayland_only || client.data.is_xwayland {
|
||||
f(&client.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -336,8 +267,6 @@ pub struct Client {
|
|||
shutdown: AsyncEvent,
|
||||
pub tracker: Tracker<Client>,
|
||||
pub is_xwayland: bool,
|
||||
pub effective_caps: Cell<ClientCaps>,
|
||||
pub bounding_caps_for_children: Cell<ClientCaps>,
|
||||
pub last_enter_serial: Cell<Option<u64>>,
|
||||
pub pid_info: PidInfo,
|
||||
pub serials: RefCell<VecDeque<SerialRange>>,
|
||||
|
|
@ -349,8 +278,8 @@ pub struct Client {
|
|||
pub wire_scale: Cell<Option<i32>>,
|
||||
pub focus_stealing_serial: Cell<Option<u64>>,
|
||||
pub changed_properties: Cell<ClMatcherChange>,
|
||||
pub destroyed: CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Rc<Self>>>>,
|
||||
pub acceptor: Rc<AcceptorMetadata>,
|
||||
pub destroyed: CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Self>>>,
|
||||
pub metadata: Rc<ClientMetadata>,
|
||||
}
|
||||
|
||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use {
|
|||
ext_image_capture_source_v1::ExtImageCaptureSourceV1,
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
head_management::jay_head_error_v1::JayHeadErrorV1,
|
||||
ipc::{
|
||||
data_transfer::{
|
||||
data_control::{
|
||||
ext_data_control_source_v1::ExtDataControlSourceV1,
|
||||
zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
|
||||
|
|
@ -19,7 +19,6 @@ use {
|
|||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
|
||||
},
|
||||
jay_output::JayOutput,
|
||||
jay_screencast::JayScreencast,
|
||||
jay_toplevel::JayToplevel,
|
||||
jay_workspace::JayWorkspace,
|
||||
wl_buffer::WlBuffer,
|
||||
|
|
@ -49,9 +48,9 @@ use {
|
|||
wire::{
|
||||
ExtDataControlSourceV1Id, ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
|
||||
ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayHeadErrorV1Id,
|
||||
JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId,
|
||||
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
|
||||
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpImageDescriptionReferenceV1Id,
|
||||
JayOutputId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId,
|
||||
WpDrmLeaseConnectorV1Id, WpImageDescriptionReferenceV1Id,
|
||||
WpImageDescriptionV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId,
|
||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
|
||||
ZwlrOutputHeadV1Id, ZwlrOutputModeV1Id, ZwpPrimarySelectionSourceV1Id,
|
||||
|
|
@ -80,7 +79,6 @@ pub struct Objects {
|
|||
pub pointers: CopyHashMap<WlPointerId, Rc<WlPointer>>,
|
||||
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
||||
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
||||
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
||||
pub timelines: CopyHashMap<WpLinuxDrmSyncobjTimelineV1Id, Rc<WpLinuxDrmSyncobjTimelineV1>>,
|
||||
pub zwlr_data_sources: CopyHashMap<ZwlrDataControlSourceV1Id, Rc<ZwlrDataControlSourceV1>>,
|
||||
pub zwlr_output_heads: CopyHashMap<ZwlrOutputHeadV1Id, Rc<ZwlrOutputHeadV1>>,
|
||||
|
|
@ -127,7 +125,6 @@ impl Objects {
|
|||
pointers: Default::default(),
|
||||
xdg_wm_bases: Default::default(),
|
||||
seats: Default::default(),
|
||||
screencasts: Default::default(),
|
||||
timelines: Default::default(),
|
||||
zwlr_data_sources: Default::default(),
|
||||
zwlr_output_heads: Default::default(),
|
||||
|
|
@ -176,7 +173,6 @@ impl Objects {
|
|||
self.xdg_wm_bases.clear();
|
||||
self.seats.clear();
|
||||
self.pointers.clear();
|
||||
self.screencasts.clear();
|
||||
self.timelines.clear();
|
||||
self.zwlr_data_sources.clear();
|
||||
self.jay_toplevels.clear();
|
||||
|
|
|
|||
331
src/clientmem.rs
331
src/clientmem.rs
|
|
@ -1,329 +1,8 @@
|
|||
use {
|
||||
crate::{
|
||||
client::Client,
|
||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
|
||||
gfx_api::{ShmMemory, ShmMemoryBacking},
|
||||
utils::{
|
||||
oserror::{OsError, OsErrorExt2},
|
||||
page_size::page_size,
|
||||
vec_ext::VecExt,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
error::Error,
|
||||
mem::{ManuallyDrop, MaybeUninit},
|
||||
ops::Deref,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
sync::atomic::{Ordering, compiler_fence},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{
|
||||
OwnedFd, Pod,
|
||||
c::{self, raise},
|
||||
ftruncate,
|
||||
},
|
||||
};
|
||||
pub use jay_clientmem::*;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ClientMemError {
|
||||
#[error("Could not install the sigbus handler")]
|
||||
SigactionFailed(#[source] crate::utils::oserror::OsError),
|
||||
#[error("A SIGBUS occurred while accessing mapped memory")]
|
||||
Sigbus,
|
||||
#[error("mmap failed")]
|
||||
MmapFailed(#[source] crate::utils::oserror::OsError),
|
||||
#[error("Length was not a multiple of the data element size")]
|
||||
InvalidLength,
|
||||
}
|
||||
|
||||
pub struct ClientMem {
|
||||
fd: ManuallyDrop<Rc<OwnedFd>>,
|
||||
failed: Cell<bool>,
|
||||
sigbus_impossible: bool,
|
||||
data: *const [Cell<u8>],
|
||||
cpu: Option<Rc<CpuWorker>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientMemOffset {
|
||||
mem: Rc<ClientMem>,
|
||||
offset: usize,
|
||||
data: *const [Cell<u8>],
|
||||
}
|
||||
|
||||
impl ClientMem {
|
||||
pub fn new(
|
||||
fd: &Rc<OwnedFd>,
|
||||
len: usize,
|
||||
read_only: bool,
|
||||
client: Option<&Client>,
|
||||
cpu: Option<&Rc<CpuWorker>>,
|
||||
is_udmabuf: bool,
|
||||
) -> Result<Self, ClientMemError> {
|
||||
Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf)
|
||||
}
|
||||
|
||||
pub fn new_private(
|
||||
fd: &Rc<OwnedFd>,
|
||||
len: usize,
|
||||
read_only: bool,
|
||||
client: Option<&Client>,
|
||||
cpu: Option<&Rc<CpuWorker>>,
|
||||
) -> Result<Self, ClientMemError> {
|
||||
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false)
|
||||
}
|
||||
|
||||
fn new2(
|
||||
fd: &Rc<OwnedFd>,
|
||||
len: usize,
|
||||
read_only: bool,
|
||||
client: Option<&Client>,
|
||||
cpu: Option<&Rc<CpuWorker>>,
|
||||
flags: c::c_int,
|
||||
is_udmabuf: bool,
|
||||
) -> Result<Self, ClientMemError> {
|
||||
let mut sigbus_impossible = is_udmabuf;
|
||||
let mut real_size = None;
|
||||
if !sigbus_impossible
|
||||
&& let Ok(seals) = uapi::fcntl_get_seals(fd.raw())
|
||||
&& seals & c::F_SEAL_SHRINK != 0
|
||||
&& let Ok(stat) = uapi::fstat(fd.raw())
|
||||
{
|
||||
real_size = Some(stat.st_size as usize);
|
||||
sigbus_impossible = stat.st_size as u64 >= len as u64;
|
||||
}
|
||||
if !sigbus_impossible && let Some(client) = client {
|
||||
log::debug!(
|
||||
"Client {} ({}) has created a shm buffer that might cause SIGBUS",
|
||||
client.pid_info.comm,
|
||||
client.id,
|
||||
);
|
||||
}
|
||||
let len = len.next_multiple_of(page_size());
|
||||
if let Some(real_size) = real_size
|
||||
&& real_size < len
|
||||
{
|
||||
let _ = ftruncate(fd.raw(), len as _);
|
||||
}
|
||||
let data = if len == 0 {
|
||||
&mut [][..]
|
||||
} else {
|
||||
let prot = match read_only {
|
||||
true => c::PROT_READ,
|
||||
false => c::PROT_READ | c::PROT_WRITE,
|
||||
};
|
||||
unsafe {
|
||||
let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0);
|
||||
if data == c::MAP_FAILED {
|
||||
return Err(ClientMemError::MmapFailed(OsError::default()));
|
||||
}
|
||||
std::slice::from_raw_parts_mut(data as *mut Cell<u8>, len)
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
fd: ManuallyDrop::new(fd.clone()),
|
||||
failed: Cell::new(false),
|
||||
sigbus_impossible,
|
||||
data,
|
||||
cpu: cpu.cloned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub fn offset(self: &Rc<Self>, offset: usize, len: usize) -> ClientMemOffset {
|
||||
let mem = unsafe { &*self.data };
|
||||
ClientMemOffset {
|
||||
mem: self.clone(),
|
||||
offset,
|
||||
data: &mem[offset..][..len],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> &Rc<OwnedFd> {
|
||||
&self.fd
|
||||
}
|
||||
|
||||
pub fn is_sealed_memfd(&self) -> bool {
|
||||
self.sigbus_impossible
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMemOffset {
|
||||
pub fn pool(&self) -> &ClientMem {
|
||||
&self.mem
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn ptr(&self) -> *const [Cell<u8>] {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn access<T, F: FnOnce(&[Cell<u8>]) -> T>(&self, f: F) -> Result<T, ClientMemError> {
|
||||
unsafe {
|
||||
if self.mem.sigbus_impossible {
|
||||
return Ok(f(&*self.data));
|
||||
}
|
||||
let mref = MemRef {
|
||||
mem: &*self.mem,
|
||||
outer: MEM.get(),
|
||||
};
|
||||
MEM.set(&mref);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
let res = f(&*self.data);
|
||||
MEM.set(mref.outer);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
match self.mem.failed.get() {
|
||||
true => Err(ClientMemError::Sigbus),
|
||||
_ => Ok(res),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read<T: Pod>(&self, dst: &mut Vec<T>) -> Result<(), ClientMemError> {
|
||||
if self.data.len().checked_rem(std::mem::size_of::<T>()) != Some(0) {
|
||||
return Err(ClientMemError::InvalidLength);
|
||||
}
|
||||
self.access(|v| {
|
||||
let len_elements = v.len() / std::mem::size_of::<T>();
|
||||
dst.reserve(len_elements);
|
||||
let (_, unused) = dst.split_at_spare_mut_bytes_ext();
|
||||
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
|
||||
unsafe {
|
||||
dst.set_len(dst.len() + len_elements);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ClientMem {
|
||||
fn drop(&mut self) {
|
||||
let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
|
||||
if let Some(cpu) = &self.cpu {
|
||||
let pending = cpu.submit(Box::new(CloseMemWork {
|
||||
fd: Rc::try_unwrap(fd).ok(),
|
||||
data: self.data,
|
||||
}));
|
||||
pending.detach();
|
||||
} else {
|
||||
unsafe {
|
||||
c::munmap(self.data as _, self.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MemRef {
|
||||
mem: *const ClientMem,
|
||||
outer: *const MemRef,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) };
|
||||
}
|
||||
|
||||
unsafe fn kill() -> ! {
|
||||
unsafe {
|
||||
c::signal(c::SIGBUS, c::SIG_DFL);
|
||||
raise(c::SIGBUS);
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) {
|
||||
unsafe {
|
||||
assert_eq!(sig, c::SIGBUS);
|
||||
let mut memr_ptr = MEM.get();
|
||||
while !memr_ptr.is_null() {
|
||||
let memr = &*memr_ptr;
|
||||
let mem = &*memr.mem;
|
||||
let lo = mem.data as *mut u8 as usize;
|
||||
let hi = lo + mem.len();
|
||||
let fault_addr = info.si_addr() as usize;
|
||||
if fault_addr < lo || fault_addr >= hi {
|
||||
memr_ptr = memr.outer;
|
||||
continue;
|
||||
}
|
||||
let res = c::mmap64(
|
||||
lo as _,
|
||||
hi - lo,
|
||||
c::PROT_WRITE | c::PROT_READ,
|
||||
c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED,
|
||||
-1,
|
||||
0,
|
||||
);
|
||||
if res == c::MAP_FAILED {
|
||||
kill();
|
||||
}
|
||||
mem.failed.set(true);
|
||||
return;
|
||||
}
|
||||
kill();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() -> Result<(), ClientMemError> {
|
||||
unsafe {
|
||||
let mut action: c::sigaction = MaybeUninit::zeroed().assume_init();
|
||||
action.sa_sigaction =
|
||||
sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _;
|
||||
action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO;
|
||||
let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut());
|
||||
uapi::map_err!(res)
|
||||
.map(drop)
|
||||
.map_os_err(ClientMemError::SigactionFailed)
|
||||
}
|
||||
}
|
||||
|
||||
struct CloseMemWork {
|
||||
fd: Option<OwnedFd>,
|
||||
data: *const [Cell<u8>],
|
||||
}
|
||||
|
||||
unsafe impl Send for CloseMemWork {}
|
||||
|
||||
impl CpuJob for CloseMemWork {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
self
|
||||
}
|
||||
|
||||
fn completed(self: Box<Self>) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWork for CloseMemWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
zone!("CloseMemWork");
|
||||
self.fd.take();
|
||||
unsafe {
|
||||
c::munmap(self.data as _, self.data.len());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl ShmMemory for ClientMemOffset {
|
||||
fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
fn safe_access(&self) -> ShmMemoryBacking {
|
||||
match self.mem.is_sealed_memfd() {
|
||||
true => ShmMemoryBacking::Ptr(self.data),
|
||||
false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset),
|
||||
}
|
||||
}
|
||||
|
||||
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
|
||||
self.access(f).map_err(|e| e.into())
|
||||
pub fn client_mem_client(client: &crate::client::Client) -> ClientMemClient<'_> {
|
||||
ClientMemClient {
|
||||
comm: &client.pid_info.comm,
|
||||
id: client.id.raw(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
src/cmm.rs
57
src/cmm.rs
|
|
@ -1,9 +1,48 @@
|
|||
pub mod cmm_description;
|
||||
pub mod cmm_eotf;
|
||||
pub mod cmm_luminance;
|
||||
pub mod cmm_manager;
|
||||
pub mod cmm_primaries;
|
||||
pub mod cmm_render_intent;
|
||||
#[cfg(test)]
|
||||
mod cmm_tests;
|
||||
pub mod cmm_transform;
|
||||
pub mod cmm_description {
|
||||
pub use jay_cmm::cmm_description::*;
|
||||
}
|
||||
|
||||
pub mod cmm_eotf {
|
||||
pub use jay_cmm::cmm_eotf::*;
|
||||
}
|
||||
|
||||
pub mod cmm_luminance {
|
||||
pub use jay_cmm::cmm_luminance::*;
|
||||
}
|
||||
|
||||
pub mod cmm_manager {
|
||||
pub use jay_cmm::cmm_manager::*;
|
||||
}
|
||||
|
||||
pub mod cmm_primaries {
|
||||
pub use jay_cmm::cmm_primaries::*;
|
||||
}
|
||||
|
||||
pub mod cmm_render_intent {
|
||||
use crate::{
|
||||
ifs::color_management::{
|
||||
ABSOLUTE_NO_ADAPTATION_SINCE, RENDER_INTENT_ABSOLUTE_NO_ADAPTATION,
|
||||
RENDER_INTENT_PERCEPTUAL, RENDER_INTENT_RELATIVE, RENDER_INTENT_RELATIVE_BPC,
|
||||
},
|
||||
object::Version,
|
||||
};
|
||||
|
||||
pub use jay_cmm::cmm_render_intent::*;
|
||||
|
||||
pub fn from_wayland(intent: u32, version: Version) -> Option<RenderIntent> {
|
||||
let res = match intent {
|
||||
RENDER_INTENT_PERCEPTUAL => RenderIntent::Perceptual,
|
||||
RENDER_INTENT_RELATIVE => RenderIntent::Relative,
|
||||
RENDER_INTENT_RELATIVE_BPC => RenderIntent::RelativeBpc,
|
||||
RENDER_INTENT_ABSOLUTE_NO_ADAPTATION if version >= ABSOLUTE_NO_ADAPTATION_SINCE => {
|
||||
RenderIntent::AbsoluteNoAdaptation
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod cmm_transform {
|
||||
pub use jay_cmm::cmm_transform::*;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{
|
||||
cmm_eotf::Eotf,
|
||||
cmm_luminance::{Luminance, TargetLuminance, white_balance},
|
||||
cmm_manager::Shared,
|
||||
cmm_primaries::{NamedPrimaries, Primaries},
|
||||
cmm_render_intent::RenderIntent,
|
||||
cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment},
|
||||
},
|
||||
utils::ordered_float::F64,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
linear_ids!(LinearColorDescriptionIds, LinearColorDescriptionId, u64);
|
||||
linear_ids!(ColorDescriptionIds, ColorDescriptionId, u64);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LinearColorDescription {
|
||||
pub id: LinearColorDescriptionId,
|
||||
pub primaries: Primaries,
|
||||
pub xyz_from_local: ColorMatrix<Xyz, Local>,
|
||||
pub local_from_xyz: ColorMatrix<Local, Xyz>,
|
||||
pub luminance: Luminance,
|
||||
pub target_primaries: Primaries,
|
||||
pub target_luminance: TargetLuminance,
|
||||
pub max_cll: Option<F64>,
|
||||
pub max_fall: Option<F64>,
|
||||
pub(super) shared: Rc<Shared>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ColorDescription {
|
||||
pub id: ColorDescriptionId,
|
||||
pub linear: Rc<LinearColorDescription>,
|
||||
pub named_primaries: Option<NamedPrimaries>,
|
||||
pub eotf: Eotf,
|
||||
pub(super) shared: Rc<Shared>,
|
||||
}
|
||||
|
||||
impl LinearColorDescription {
|
||||
pub fn color_transform(&self, target: &Self, intent: RenderIntent) -> ColorMatrix {
|
||||
let mut mat = target.local_from_xyz;
|
||||
if self.luminance != target.luminance {
|
||||
mat *= white_balance(
|
||||
&self.luminance,
|
||||
&target.luminance,
|
||||
target.primaries.wp,
|
||||
intent,
|
||||
);
|
||||
}
|
||||
if self.primaries.wp != target.primaries.wp && intent.bradford_adjustment() {
|
||||
mat *= bradford_adjustment(self.primaries.wp, target.primaries.wp);
|
||||
}
|
||||
mat * self.xyz_from_local
|
||||
}
|
||||
|
||||
pub fn embeds_into(&self, target: &Self) -> bool {
|
||||
if self.id == target.id {
|
||||
return true;
|
||||
}
|
||||
if self.primaries != target.primaries {
|
||||
return false;
|
||||
}
|
||||
if self.luminance != target.luminance {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorDescription {
|
||||
pub fn embeds_into(&self, target: &Self) -> bool {
|
||||
self.eotf == target.eotf && self.linear.embeds_into(&target.linear)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LinearColorDescription {
|
||||
fn drop(&mut self) {
|
||||
self.shared.dead_linear.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ColorDescription {
|
||||
fn drop(&mut self) {
|
||||
self.shared.dead_complete.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
use crate::utils::ordered_float::F32;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Eotf {
|
||||
Linear,
|
||||
St2084Pq,
|
||||
Bt1886(F32),
|
||||
Gamma22,
|
||||
Gamma24,
|
||||
Gamma28,
|
||||
St240,
|
||||
Log100,
|
||||
Log316,
|
||||
St428,
|
||||
Pow(EotfPow),
|
||||
CompoundPower24,
|
||||
}
|
||||
|
||||
const MUL: u32 = 10_000;
|
||||
const MUL_F32: f32 = MUL as f32;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct EotfPow(pub u32);
|
||||
|
||||
impl EotfPow {
|
||||
pub const MIN: Self = Self(10_000);
|
||||
pub const LINEAR: Self = Self(10_000);
|
||||
pub const GAMMA22: Self = Self(22_000);
|
||||
pub const GAMMA24: Self = Self(24_000);
|
||||
pub const GAMMA28: Self = Self(28_000);
|
||||
pub const MAX: Self = Self(100_000);
|
||||
|
||||
pub fn eotf_f32(self) -> f32 {
|
||||
self.0 as f32 / MUL_F32
|
||||
}
|
||||
|
||||
pub fn inv_eotf_f32(self) -> f32 {
|
||||
MUL_F32 / self.0 as f32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bt1886_eotf_args(c: F32) -> [f32; 4] {
|
||||
let c = c.0;
|
||||
let gamma = 1.0 / 2.4;
|
||||
let a1 = 1.0 / (1.0 - c);
|
||||
let a2 = 1.0 - c.powf(gamma);
|
||||
let a3 = c.powf(gamma);
|
||||
let a4 = c;
|
||||
[a1, a2, a3, a4]
|
||||
}
|
||||
|
||||
pub fn bt1886_inv_eotf_args(c: F32) -> [f32; 4] {
|
||||
let c = c.0;
|
||||
let gamma = 1.0 / 2.4;
|
||||
let a1 = 1.0 / (1.0 - c.powf(gamma));
|
||||
let a2 = 1.0 - c;
|
||||
let a3 = c;
|
||||
let a4 = c.powf(gamma);
|
||||
[a1, a2, a3, a4]
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
use crate::{
|
||||
cmm::{
|
||||
cmm_render_intent::RenderIntent,
|
||||
cmm_transform::{ColorMatrix, Xyz},
|
||||
},
|
||||
utils::ordered_float::F64,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct Luminance {
|
||||
pub min: F64,
|
||||
pub max: F64,
|
||||
pub white: F64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct TargetLuminance {
|
||||
pub min: F64,
|
||||
pub max: F64,
|
||||
}
|
||||
|
||||
impl Luminance {
|
||||
pub const SRGB: Self = Self {
|
||||
min: F64(0.2),
|
||||
max: F64(80.0),
|
||||
white: F64(80.0),
|
||||
};
|
||||
|
||||
pub const BT1886: Self = Self {
|
||||
min: F64(0.01),
|
||||
max: F64(100.0),
|
||||
white: F64(100.0),
|
||||
};
|
||||
|
||||
pub const ST2084_PQ: Self = Self {
|
||||
min: F64(0.0),
|
||||
max: F64(10000.0),
|
||||
white: F64(203.0),
|
||||
};
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub const HLG: Self = Self {
|
||||
min: F64(0.005),
|
||||
max: F64(1000.0),
|
||||
white: F64(203.0),
|
||||
};
|
||||
|
||||
pub const WINDOWS_SCRGB: Self = Self {
|
||||
min: Self::ST2084_PQ.min,
|
||||
max: Self::ST2084_PQ.max,
|
||||
// This causes the white balance formula (with target ST2084_PQ) to simplify to
|
||||
// `Y * 80 / 10000`, meaning that sRGB pure white maps to a luminance of
|
||||
// 80 cd/m^2.
|
||||
white: F64(Self::ST2084_PQ.white.0 / 80.0 * Self::ST2084_PQ.max.0),
|
||||
};
|
||||
}
|
||||
|
||||
impl Luminance {
|
||||
pub fn to_target(&self) -> TargetLuminance {
|
||||
TargetLuminance {
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Luminance {
|
||||
fn default() -> Self {
|
||||
Self::SRGB
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(non_snake_case)]
|
||||
pub fn white_balance(
|
||||
from: &Luminance,
|
||||
to: &Luminance,
|
||||
w_to: (F64, F64),
|
||||
intent: RenderIntent,
|
||||
) -> ColorMatrix<Xyz, Xyz> {
|
||||
let a = ((from.max - from.min) / (to.max - to.min) * (to.white - to.min)
|
||||
/ (from.white - from.min))
|
||||
.0;
|
||||
let d = match intent.black_point_compensation() {
|
||||
true => 0.0,
|
||||
false => ((from.min - to.min) / (to.max - to.min)).0,
|
||||
};
|
||||
let s = a - d;
|
||||
let (F64(x_to), F64(y_to)) = w_to;
|
||||
let X_to = x_to / y_to;
|
||||
let Y_to = 1.0;
|
||||
let Z_to = (1.0 - x_to - y_to) / y_to;
|
||||
ColorMatrix::new([
|
||||
[s, 0.0, 0.0, d * X_to],
|
||||
[0.0, s, 0.0, d * Y_to],
|
||||
[0.0, 0.0, s, d * Z_to],
|
||||
])
|
||||
}
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{
|
||||
cmm_description::{
|
||||
ColorDescription, ColorDescriptionIds, LinearColorDescription,
|
||||
LinearColorDescriptionId, LinearColorDescriptionIds,
|
||||
},
|
||||
cmm_eotf::Eotf,
|
||||
cmm_luminance::{Luminance, TargetLuminance},
|
||||
cmm_primaries::{NamedPrimaries, Primaries},
|
||||
},
|
||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell, ordered_float::F64},
|
||||
},
|
||||
std::rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
pub struct ColorManager {
|
||||
linear_ids: LinearColorDescriptionIds,
|
||||
linear_descriptions: CopyHashMap<LinearDescriptionKey, Weak<LinearColorDescription>>,
|
||||
complete_descriptions: CopyHashMap<CompleteDescriptionKey, Weak<ColorDescription>>,
|
||||
shared: Rc<Shared>,
|
||||
srgb_gamma22: Rc<ColorDescription>,
|
||||
srgb_linear: Rc<ColorDescription>,
|
||||
windows_scrgb: Rc<ColorDescription>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct Shared {
|
||||
pub(super) dead_linear: NumCell<usize>,
|
||||
pub(super) dead_complete: NumCell<usize>,
|
||||
pub(super) complete_ids: ColorDescriptionIds,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct LinearDescriptionKey {
|
||||
primaries: Primaries,
|
||||
luminance: Luminance,
|
||||
target_primaries: Primaries,
|
||||
target_luminance: TargetLuminance,
|
||||
max_cll: Option<F64>,
|
||||
max_fall: Option<F64>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct CompleteDescriptionKey {
|
||||
linear: LinearColorDescriptionId,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
eotf: Eotf,
|
||||
}
|
||||
|
||||
impl ColorManager {
|
||||
pub fn new() -> Rc<Self> {
|
||||
let linear_ids = LinearColorDescriptionIds::default();
|
||||
let linear_descriptions = CopyHashMap::default();
|
||||
let complete_descriptions = CopyHashMap::default();
|
||||
let shared = Rc::new(Shared::default());
|
||||
let _ = shared.complete_ids.next();
|
||||
let srgb_gamma22 = get_description(
|
||||
&shared,
|
||||
&linear_descriptions,
|
||||
&complete_descriptions,
|
||||
&linear_ids,
|
||||
Some(NamedPrimaries::Srgb),
|
||||
Primaries::SRGB,
|
||||
Luminance::SRGB,
|
||||
Eotf::Gamma22,
|
||||
Primaries::SRGB,
|
||||
Luminance::SRGB.to_target(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let srgb_linear = get_description2(
|
||||
&shared,
|
||||
&srgb_gamma22.linear,
|
||||
&complete_descriptions,
|
||||
Some(NamedPrimaries::Srgb),
|
||||
Eotf::Linear,
|
||||
);
|
||||
let windows_scrgb = get_description(
|
||||
&shared,
|
||||
&linear_descriptions,
|
||||
&complete_descriptions,
|
||||
&linear_ids,
|
||||
Some(NamedPrimaries::Srgb),
|
||||
Primaries::SRGB,
|
||||
Luminance::WINDOWS_SCRGB,
|
||||
Eotf::Linear,
|
||||
Primaries::BT2020,
|
||||
Luminance::ST2084_PQ.to_target(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Rc::new(Self {
|
||||
linear_ids,
|
||||
linear_descriptions,
|
||||
complete_descriptions,
|
||||
shared,
|
||||
srgb_gamma22,
|
||||
srgb_linear,
|
||||
windows_scrgb,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn srgb_gamma22(&self) -> &Rc<ColorDescription> {
|
||||
&self.srgb_gamma22
|
||||
}
|
||||
|
||||
pub fn srgb_linear(&self) -> &Rc<ColorDescription> {
|
||||
&self.srgb_linear
|
||||
}
|
||||
|
||||
pub fn windows_scrgb(&self) -> &Rc<ColorDescription> {
|
||||
&self.windows_scrgb
|
||||
}
|
||||
|
||||
pub fn get_description(
|
||||
self: &Rc<Self>,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
primaries: Primaries,
|
||||
luminance: Luminance,
|
||||
eotf: Eotf,
|
||||
target_primaries: Primaries,
|
||||
target_luminance: TargetLuminance,
|
||||
max_cll: Option<F64>,
|
||||
max_fall: Option<F64>,
|
||||
) -> Rc<ColorDescription> {
|
||||
get_description(
|
||||
&self.shared,
|
||||
&self.linear_descriptions,
|
||||
&self.complete_descriptions,
|
||||
&self.linear_ids,
|
||||
named_primaries,
|
||||
primaries,
|
||||
luminance,
|
||||
eotf,
|
||||
target_primaries,
|
||||
target_luminance,
|
||||
max_cll,
|
||||
max_fall,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_with_tf(
|
||||
self: &Rc<Self>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
eotf: Eotf,
|
||||
) -> Rc<ColorDescription> {
|
||||
get_description2(
|
||||
&self.shared,
|
||||
&cd.linear,
|
||||
&self.complete_descriptions,
|
||||
cd.named_primaries,
|
||||
eotf,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_description(
|
||||
shared: &Rc<Shared>,
|
||||
linear_descriptions: &CopyHashMap<LinearDescriptionKey, Weak<LinearColorDescription>>,
|
||||
complete_descriptions: &CopyHashMap<CompleteDescriptionKey, Weak<ColorDescription>>,
|
||||
linear_ids: &LinearColorDescriptionIds,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
primaries: Primaries,
|
||||
luminance: Luminance,
|
||||
eotf: Eotf,
|
||||
target_primaries: Primaries,
|
||||
target_luminance: TargetLuminance,
|
||||
max_cll: Option<F64>,
|
||||
max_fall: Option<F64>,
|
||||
) -> Rc<ColorDescription> {
|
||||
macro_rules! gc {
|
||||
($d:ident, $i:expr) => {
|
||||
if $d.len() > 16 && $i.get() * 2 > $d.len() {
|
||||
$d.lock().retain(|_, d| d.strong_count() > 0);
|
||||
$i.set(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
gc!(linear_descriptions, &shared.dead_linear);
|
||||
gc!(complete_descriptions, &shared.dead_complete);
|
||||
let key = LinearDescriptionKey {
|
||||
primaries,
|
||||
luminance,
|
||||
target_primaries,
|
||||
target_luminance,
|
||||
max_cll,
|
||||
max_fall,
|
||||
};
|
||||
if let Some(d) = linear_descriptions.get(&key) {
|
||||
if let Some(d) = d.upgrade() {
|
||||
return get_description2(shared, &d, complete_descriptions, named_primaries, eotf);
|
||||
}
|
||||
shared.dead_linear.fetch_sub(1);
|
||||
}
|
||||
let (xyz_from_local, local_from_xyz) = primaries.matrices();
|
||||
let d = Rc::new(LinearColorDescription {
|
||||
id: linear_ids.next(),
|
||||
primaries,
|
||||
xyz_from_local,
|
||||
local_from_xyz,
|
||||
luminance,
|
||||
target_primaries,
|
||||
target_luminance,
|
||||
max_cll,
|
||||
max_fall,
|
||||
shared: shared.clone(),
|
||||
});
|
||||
linear_descriptions.set(key, Rc::downgrade(&d));
|
||||
let key = CompleteDescriptionKey {
|
||||
linear: d.id,
|
||||
named_primaries,
|
||||
eotf,
|
||||
};
|
||||
let d = Rc::new(ColorDescription {
|
||||
id: shared.complete_ids.next(),
|
||||
linear: d,
|
||||
named_primaries,
|
||||
eotf,
|
||||
shared: shared.clone(),
|
||||
});
|
||||
complete_descriptions.set(key, Rc::downgrade(&d));
|
||||
d
|
||||
}
|
||||
|
||||
fn get_description2(
|
||||
shared: &Rc<Shared>,
|
||||
ld: &Rc<LinearColorDescription>,
|
||||
complete_descriptions: &CopyHashMap<CompleteDescriptionKey, Weak<ColorDescription>>,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
eotf: Eotf,
|
||||
) -> Rc<ColorDescription> {
|
||||
let key = CompleteDescriptionKey {
|
||||
linear: ld.id,
|
||||
named_primaries,
|
||||
eotf,
|
||||
};
|
||||
if let Some(d) = complete_descriptions.get(&key) {
|
||||
if let Some(d) = d.upgrade() {
|
||||
return d;
|
||||
}
|
||||
shared.dead_complete.fetch_sub(1);
|
||||
}
|
||||
let d = Rc::new(ColorDescription {
|
||||
id: shared.complete_ids.next(),
|
||||
linear: ld.clone(),
|
||||
named_primaries,
|
||||
eotf,
|
||||
shared: shared.clone(),
|
||||
});
|
||||
complete_descriptions.set(key, Rc::downgrade(&d));
|
||||
d
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
use {crate::utils::ordered_float::F64, std::hash::Hash};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum NamedPrimaries {
|
||||
Srgb,
|
||||
PalM,
|
||||
Pal,
|
||||
Ntsc,
|
||||
GenericFilm,
|
||||
Bt2020,
|
||||
Cie1931Xyz,
|
||||
DciP3,
|
||||
DisplayP3,
|
||||
AdobeRgb,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct Primaries {
|
||||
pub r: (F64, F64),
|
||||
pub g: (F64, F64),
|
||||
pub b: (F64, F64),
|
||||
pub wp: (F64, F64),
|
||||
}
|
||||
|
||||
impl Primaries {
|
||||
pub const SRGB: Self = Self {
|
||||
r: (F64(0.64), F64(0.33)),
|
||||
g: (F64(0.3), F64(0.6)),
|
||||
b: (F64(0.15), F64(0.06)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const PAL_M: Self = Self {
|
||||
r: (F64(0.67), F64(0.33)),
|
||||
g: (F64(0.21), F64(0.71)),
|
||||
b: (F64(0.14), F64(0.08)),
|
||||
wp: (F64(0.310), F64(0.316)),
|
||||
};
|
||||
|
||||
pub const PAL: Self = Self {
|
||||
r: (F64(0.64), F64(0.33)),
|
||||
g: (F64(0.29), F64(0.60)),
|
||||
b: (F64(0.15), F64(0.06)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const NTSC: Self = Self {
|
||||
r: (F64(0.630), F64(0.340)),
|
||||
g: (F64(0.310), F64(0.595)),
|
||||
b: (F64(0.155), F64(0.070)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const GENERIC_FILM: Self = Self {
|
||||
r: (F64(0.681), F64(0.319)),
|
||||
g: (F64(0.243), F64(0.692)),
|
||||
b: (F64(0.145), F64(0.049)),
|
||||
wp: (F64(0.310), F64(0.316)),
|
||||
};
|
||||
|
||||
pub const BT2020: Self = Self {
|
||||
r: (F64(0.708), F64(0.292)),
|
||||
g: (F64(0.170), F64(0.797)),
|
||||
b: (F64(0.131), F64(0.046)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const CIE1931_XYZ: Self = Self {
|
||||
r: (F64(1.0), F64(0.0)),
|
||||
g: (F64(0.0), F64(1.0)),
|
||||
b: (F64(0.0), F64(0.0)),
|
||||
wp: (F64(1.0 / 3.0), F64(1.0 / 3.0)),
|
||||
};
|
||||
|
||||
pub const DCI_P3: Self = Self {
|
||||
r: (F64(0.680), F64(0.320)),
|
||||
g: (F64(0.265), F64(0.690)),
|
||||
b: (F64(0.150), F64(0.060)),
|
||||
wp: (F64(0.314), F64(0.351)),
|
||||
};
|
||||
|
||||
pub const DISPLAY_P3: Self = Self {
|
||||
r: (F64(0.680), F64(0.320)),
|
||||
g: (F64(0.265), F64(0.690)),
|
||||
b: (F64(0.150), F64(0.060)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const ADOBE_RGB: Self = Self {
|
||||
r: (F64(0.64), F64(0.33)),
|
||||
g: (F64(0.21), F64(0.71)),
|
||||
b: (F64(0.15), F64(0.06)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
}
|
||||
impl NamedPrimaries {
|
||||
pub const fn primaries(self) -> Primaries {
|
||||
match self {
|
||||
NamedPrimaries::Srgb => Primaries::SRGB,
|
||||
NamedPrimaries::PalM => Primaries::PAL_M,
|
||||
NamedPrimaries::Pal => Primaries::PAL,
|
||||
NamedPrimaries::Ntsc => Primaries::NTSC,
|
||||
NamedPrimaries::GenericFilm => Primaries::GENERIC_FILM,
|
||||
NamedPrimaries::Bt2020 => Primaries::BT2020,
|
||||
NamedPrimaries::Cie1931Xyz => Primaries::CIE1931_XYZ,
|
||||
NamedPrimaries::DciP3 => Primaries::DCI_P3,
|
||||
NamedPrimaries::DisplayP3 => Primaries::DISPLAY_P3,
|
||||
NamedPrimaries::AdobeRgb => Primaries::ADOBE_RGB,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
use crate::{
|
||||
ifs::color_management::{
|
||||
ABSOLUTE_NO_ADAPTATION_SINCE, RENDER_INTENT_ABSOLUTE_NO_ADAPTATION,
|
||||
RENDER_INTENT_PERCEPTUAL, RENDER_INTENT_RELATIVE, RENDER_INTENT_RELATIVE_BPC,
|
||||
},
|
||||
object::Version,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||
pub enum RenderIntent {
|
||||
#[default]
|
||||
Perceptual,
|
||||
Relative,
|
||||
RelativeBpc,
|
||||
AbsoluteNoAdaptation,
|
||||
}
|
||||
|
||||
impl RenderIntent {
|
||||
pub fn from_wayland(intent: u32, version: Version) -> Option<Self> {
|
||||
let res = match intent {
|
||||
RENDER_INTENT_PERCEPTUAL => Self::Perceptual,
|
||||
RENDER_INTENT_RELATIVE => Self::Relative,
|
||||
RENDER_INTENT_RELATIVE_BPC => Self::RelativeBpc,
|
||||
RENDER_INTENT_ABSOLUTE_NO_ADAPTATION if version >= ABSOLUTE_NO_ADAPTATION_SINCE => {
|
||||
Self::AbsoluteNoAdaptation
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn black_point_compensation(self) -> bool {
|
||||
match self {
|
||||
RenderIntent::Perceptual => true,
|
||||
RenderIntent::RelativeBpc => true,
|
||||
RenderIntent::Relative => false,
|
||||
RenderIntent::AbsoluteNoAdaptation => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bradford_adjustment(self) -> bool {
|
||||
match self {
|
||||
RenderIntent::Perceptual => true,
|
||||
RenderIntent::RelativeBpc => true,
|
||||
RenderIntent::Relative => true,
|
||||
RenderIntent::AbsoluteNoAdaptation => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
mod matrices {
|
||||
use crate::{cmm::cmm_primaries::Primaries, utils::ordered_float::F64};
|
||||
|
||||
fn check(primaries: Primaries, expected: [[f64; 4]; 3]) {
|
||||
let (ltg, gtl) = primaries.matrices();
|
||||
println!("{:#?}", ltg);
|
||||
assert!((ltg.0[0][0].0 - expected[0][0]).abs() < 0.001);
|
||||
assert!((ltg.0[0][1].0 - expected[0][1]).abs() < 0.001);
|
||||
assert!((ltg.0[0][2].0 - expected[0][2]).abs() < 0.001);
|
||||
assert!((ltg.0[0][3].0 - expected[0][3]).abs() < 0.001);
|
||||
assert!((ltg.0[1][0].0 - expected[1][0]).abs() < 0.001);
|
||||
assert!((ltg.0[1][1].0 - expected[1][1]).abs() < 0.001);
|
||||
assert!((ltg.0[1][2].0 - expected[1][2]).abs() < 0.001);
|
||||
assert!((ltg.0[1][3].0 - expected[1][3]).abs() < 0.001);
|
||||
assert!((ltg.0[2][0].0 - expected[2][0]).abs() < 0.001);
|
||||
assert!((ltg.0[2][1].0 - expected[2][1]).abs() < 0.001);
|
||||
assert!((ltg.0[2][2].0 - expected[2][2]).abs() < 0.001);
|
||||
assert!((ltg.0[2][3].0 - expected[2][3]).abs() < 0.001);
|
||||
let roundtrip = gtl * ltg;
|
||||
assert!((roundtrip.0[0][0].0 - 1.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[0][1].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[0][2].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[0][3].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][0].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][1].0 - 1.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][2].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][3].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][0].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][1].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][2].0 - 1.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][3].0 - 0.0).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srgb() {
|
||||
check(
|
||||
Primaries::SRGB,
|
||||
[
|
||||
[0.4124564, 0.3575761, 0.1804375, 0.0],
|
||||
[0.2126729, 0.7151522, 0.0721750, 0.0],
|
||||
[0.0193339, 0.1191920, 0.9503041, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cie1931_xyz() {
|
||||
check(
|
||||
Primaries::CIE1931_XYZ,
|
||||
[
|
||||
[1.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adobe_rgb() {
|
||||
check(
|
||||
Primaries::ADOBE_RGB,
|
||||
[
|
||||
[0.5767309, 0.1855540, 0.1881852, 0.0],
|
||||
[0.2973769, 0.6273491, 0.0752741, 0.0],
|
||||
[0.0270343, 0.0706872, 0.9911085, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apple_rgb() {
|
||||
check(
|
||||
Primaries {
|
||||
r: (F64(0.625), F64(0.34)),
|
||||
g: (F64(0.28), F64(0.595)),
|
||||
b: (F64(0.155), F64(0.07)),
|
||||
wp: (F64(0.31271), F64(0.32902)),
|
||||
},
|
||||
[
|
||||
[0.4497288, 0.3162486, 0.1844926, 0.0],
|
||||
[0.2446525, 0.6720283, 0.0833192, 0.0],
|
||||
[0.0251848, 0.1411824, 0.9224628, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bt2020() {
|
||||
check(
|
||||
Primaries::BT2020,
|
||||
[
|
||||
[0.636958, 0.144617, 0.168881, 0.0],
|
||||
[0.262700, 0.677998, 0.059302, 0.0],
|
||||
[0.000000, 0.028073, 1.060985, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pal() {
|
||||
check(
|
||||
Primaries::PAL,
|
||||
[
|
||||
[0.4306190, 0.3415419, 0.1783091, 0.0],
|
||||
[0.2220379, 0.7066384, 0.0713236, 0.0],
|
||||
[0.0201853, 0.1295504, 0.9390944, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dci_p3() {
|
||||
check(
|
||||
Primaries::DCI_P3,
|
||||
[
|
||||
[0.445170, 0.277134, 0.172283, 0.0],
|
||||
[0.209492, 0.721595, 0.068913, 0.0],
|
||||
[-0.000000, 0.047061, 0.907355, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_p3() {
|
||||
check(
|
||||
Primaries::DISPLAY_P3,
|
||||
[
|
||||
[0.486571, 0.265668, 0.198217, 0.0],
|
||||
[0.228975, 0.691739, 0.079287, 0.0],
|
||||
[-0.000000, 0.045113, 1.043944, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod transforms {
|
||||
use crate::cmm::{
|
||||
cmm_eotf::Eotf, cmm_luminance::Luminance, cmm_manager::ColorManager,
|
||||
cmm_primaries::Primaries, cmm_render_intent::RenderIntent,
|
||||
};
|
||||
|
||||
fn check(p1: Primaries, p2: Primaries, expected: [[f64; 4]; 3]) {
|
||||
let manager = ColorManager::new();
|
||||
let d = |p| {
|
||||
manager.get_description(
|
||||
None,
|
||||
p,
|
||||
Luminance::SRGB,
|
||||
Eotf::Linear,
|
||||
p,
|
||||
Luminance::SRGB.to_target(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let d1 = d(p1);
|
||||
let d2 = d(p2);
|
||||
let m = d1
|
||||
.linear
|
||||
.color_transform(&d2.linear, RenderIntent::Perceptual);
|
||||
println!("{:#?}", m);
|
||||
assert!((m.0[0][0].0 - expected[0][0]).abs() < 0.001);
|
||||
assert!((m.0[0][1].0 - expected[0][1]).abs() < 0.001);
|
||||
assert!((m.0[0][2].0 - expected[0][2]).abs() < 0.001);
|
||||
assert!((m.0[0][3].0 - expected[0][3]).abs() < 0.001);
|
||||
assert!((m.0[1][0].0 - expected[1][0]).abs() < 0.001);
|
||||
assert!((m.0[1][1].0 - expected[1][1]).abs() < 0.001);
|
||||
assert!((m.0[1][2].0 - expected[1][2]).abs() < 0.001);
|
||||
assert!((m.0[1][3].0 - expected[1][3]).abs() < 0.001);
|
||||
assert!((m.0[2][0].0 - expected[2][0]).abs() < 0.001);
|
||||
assert!((m.0[2][1].0 - expected[2][1]).abs() < 0.001);
|
||||
assert!((m.0[2][2].0 - expected[2][2]).abs() < 0.001);
|
||||
assert!((m.0[2][3].0 - expected[2][3]).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srgb_to_bt2020() {
|
||||
check(
|
||||
Primaries::SRGB,
|
||||
Primaries::BT2020,
|
||||
[
|
||||
[0.627404, 0.329283, 0.043313, 0.0],
|
||||
[0.069097, 0.919540, 0.011362, 0.0],
|
||||
[0.016391, 0.088013, 0.895595, 0.0],
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bt2020_to_srgb() {
|
||||
check(
|
||||
Primaries::BT2020,
|
||||
Primaries::SRGB,
|
||||
[
|
||||
[1.660491, -0.587641, -0.072850, 0.0],
|
||||
[-0.124550, 1.132900, -0.008349, 0.0],
|
||||
[-0.018151, -0.100579, 1.118730, 0.0],
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srgb_to_dci_p3() {
|
||||
check(
|
||||
Primaries::SRGB,
|
||||
Primaries::DCI_P3,
|
||||
[
|
||||
[0.868580, 0.128919, 0.002501, 0.0],
|
||||
[0.034540, 0.961811, 0.003648, 0.0],
|
||||
[0.016771, 0.071040, 0.912189, 0.0],
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{cmm_eotf::Eotf, cmm_primaries::Primaries},
|
||||
gfx_api::AlphaMode,
|
||||
theme::Color,
|
||||
utils::ordered_float::F64,
|
||||
},
|
||||
std::{
|
||||
fmt,
|
||||
fmt::{Debug, Formatter},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::{Mul, MulAssign},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ColorMatrix<To = Local, From = Local>(pub [[F64; 4]; 3], PhantomData<(To, From)>);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Local;
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Xyz;
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Bradford;
|
||||
|
||||
impl<T, U> Copy for ColorMatrix<T, U> {}
|
||||
|
||||
impl<T, U> Clone for ColorMatrix<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<Self> for ColorMatrix<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Eq for ColorMatrix<T, U> {}
|
||||
|
||||
impl<T, U> Hash for ColorMatrix<T, U> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Debug for ColorMatrix<T, U> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("ColorMatrix")
|
||||
.field(&format_matrix(&self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn format_matrix<'a>(m: &'a [[F64; 4]; 3]) -> impl Debug + use<'a> {
|
||||
fmt::from_fn(move |f| {
|
||||
let iter = m
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(Some([F64(0.0), F64(0.0), F64(0.0), F64(1.0)]))
|
||||
.enumerate();
|
||||
if f.alternate() {
|
||||
for (idx, row) in iter {
|
||||
if idx > 0 {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"{:7.4} {:7.4} {:7.4} {:7.4}",
|
||||
row[0], row[1], row[2], row[3]
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
f.write_str("[")?;
|
||||
for (idx, row) in iter {
|
||||
if idx > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"[{:.4}, {:.4}, {:.4}, {:.4}]",
|
||||
row[0], row[1], row[2], row[3]
|
||||
)?;
|
||||
}
|
||||
f.write_str("]")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
impl<T, U, V> Mul<ColorMatrix<U, T>> for ColorMatrix<V, U> {
|
||||
type Output = ColorMatrix<V, T>;
|
||||
|
||||
fn mul(self, rhs: ColorMatrix<U, T>) -> Self::Output {
|
||||
let a = &self.0;
|
||||
let b = &rhs.0;
|
||||
macro_rules! mul {
|
||||
($ar:expr, $bc:expr) => {
|
||||
a[$ar][0] * b[0][$bc] + a[$ar][1] * b[1][$bc] + a[$ar][2] * b[2][$bc]
|
||||
};
|
||||
}
|
||||
let m = [
|
||||
[mul!(0, 0), mul!(0, 1), mul!(0, 2), mul!(0, 3) + a[0][3]],
|
||||
[mul!(1, 0), mul!(1, 1), mul!(1, 2), mul!(1, 3) + a[1][3]],
|
||||
[mul!(2, 0), mul!(2, 1), mul!(2, 2), mul!(2, 3) + a[2][3]],
|
||||
];
|
||||
ColorMatrix(m, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, V> MulAssign<ColorMatrix<U, U>> for ColorMatrix<V, U> {
|
||||
fn mul_assign(&mut self, rhs: ColorMatrix<U, U>) {
|
||||
*self = *self * rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Mul<[f64; 3]> for ColorMatrix<T, U> {
|
||||
type Output = [f64; 3];
|
||||
|
||||
fn mul(self, rhs: [f64; 3]) -> Self::Output {
|
||||
let a = &self.0;
|
||||
macro_rules! mul {
|
||||
($ar:expr) => {
|
||||
a[$ar][0].0 * rhs[0] + a[$ar][1].0 * rhs[1] + a[$ar][2].0 * rhs[2]
|
||||
};
|
||||
}
|
||||
[mul!(0), mul!(1), mul!(2)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Mul<Color> for ColorMatrix<T, U> {
|
||||
type Output = Color;
|
||||
|
||||
fn mul(self, rhs: Color) -> Self::Output {
|
||||
let mut rgba = rhs.to_array(Eotf::Linear);
|
||||
let a = rgba[3];
|
||||
if a < 1.0 && a > 0.0 {
|
||||
for c in &mut rgba[..3] {
|
||||
*c /= a;
|
||||
}
|
||||
}
|
||||
let [r, g, b] = self * [rgba[0] as f64, rgba[1] as f64, rgba[2] as f64];
|
||||
Color::new(
|
||||
Eotf::Linear,
|
||||
AlphaMode::Straight,
|
||||
r as f32,
|
||||
g as f32,
|
||||
b as f32,
|
||||
a,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ColorMatrix<T, U> {
|
||||
pub const fn new(m: [[f64; 4]; 3]) -> Self {
|
||||
let m = [
|
||||
[F64(m[0][0]), F64(m[0][1]), F64(m[0][2]), F64(m[0][3])],
|
||||
[F64(m[1][0]), F64(m[1][1]), F64(m[1][2]), F64(m[1][3])],
|
||||
[F64(m[2][0]), F64(m[2][1]), F64(m[2][2]), F64(m[2][3])],
|
||||
];
|
||||
Self(m, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn to_f32(&self) -> [[f32; 4]; 4] {
|
||||
let m = &self.0;
|
||||
macro_rules! map {
|
||||
($r:expr, $c:expr) => {
|
||||
m[$r][$c].0 as f32
|
||||
};
|
||||
}
|
||||
[
|
||||
[map!(0, 0), map!(0, 1), map!(0, 2), map!(0, 3)],
|
||||
[map!(1, 0), map!(1, 1), map!(1, 2), map!(1, 3)],
|
||||
[map!(2, 0), map!(2, 1), map!(2, 2), map!(2, 3)],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorMatrix<Bradford, Xyz> {
|
||||
const BFD: Self = Self::new([
|
||||
[0.8951, 0.2664, -0.1614, 0.0],
|
||||
[-0.7502, 1.7135, 0.0367, 0.0],
|
||||
[0.0389, -0.0685, 1.0296, 0.0],
|
||||
]);
|
||||
}
|
||||
|
||||
impl ColorMatrix<Xyz, Bradford> {
|
||||
const BFD_INV: Self = Self::new([
|
||||
[0.9870, -0.1471, 0.1600, 0.0],
|
||||
[0.4323, 0.5184, 0.0493, 0.0],
|
||||
[-0.0085, 0.04, 0.9685, 0.0],
|
||||
]);
|
||||
}
|
||||
|
||||
#[expect(non_snake_case)]
|
||||
pub fn bradford_adjustment(w_from: (F64, F64), w_to: (F64, F64)) -> ColorMatrix<Xyz, Xyz> {
|
||||
let (F64(x_from), F64(y_from)) = w_from;
|
||||
let (F64(x_to), F64(y_to)) = w_to;
|
||||
let X_from = x_from / y_from;
|
||||
let Z_from = (1.0 - x_from - y_from) / y_from;
|
||||
let X_to = x_to / y_to;
|
||||
let Z_to = (1.0 - x_to - y_to) / y_to;
|
||||
let [R_from, G_from, B_from] = ColorMatrix::BFD * [X_from, 1.0, Z_from];
|
||||
let [R_to, G_to, B_to] = ColorMatrix::BFD * [X_to, 1.0, Z_to];
|
||||
let adj = ColorMatrix::new([
|
||||
[R_to / R_from, 0.0, 0.0, 0.0],
|
||||
[0.0, G_to / G_from, 0.0, 0.0],
|
||||
[0.0, 0.0, B_to / B_from, 0.0],
|
||||
]);
|
||||
ColorMatrix::BFD_INV * adj * ColorMatrix::BFD
|
||||
}
|
||||
|
||||
impl Primaries {
|
||||
#[expect(non_snake_case)]
|
||||
pub const fn matrices(&self) -> (ColorMatrix<Xyz, Local>, ColorMatrix<Local, Xyz>) {
|
||||
let (F64(xw), F64(yw)) = self.wp;
|
||||
let Xw = xw / yw;
|
||||
let Zw = (1.0 - xw - yw) / yw;
|
||||
let (F64(xr), F64(yr)) = self.r;
|
||||
let (F64(xg), F64(yg)) = self.g;
|
||||
let (F64(xb), F64(yb)) = self.b;
|
||||
let zr = 1.0 - xr - yr;
|
||||
let zg = 1.0 - xg - yg;
|
||||
let zb = 1.0 - xb - yb;
|
||||
let srx = yg * zb - zg * yb;
|
||||
let sry = zg * xb - xg * zb;
|
||||
let srz = xg * yb - yg * xb;
|
||||
let sgx = zr * yb - yr * zb;
|
||||
let sgz = yr * xb - xr * yb;
|
||||
let sgy = xr * zb - zr * xb;
|
||||
let sbx = yr * zg - zr * yg;
|
||||
let sby = zr * xg - xr * zg;
|
||||
let sbz = xr * yg - yr * xg;
|
||||
let det = srz + sgz + sbz;
|
||||
let sr = srx * Xw + sry + srz * Zw;
|
||||
let sg = sgx * Xw + sgy + sgz * Zw;
|
||||
let sb = sbx * Xw + sby + sbz * Zw;
|
||||
let det_inv = 1.0 / det;
|
||||
let sr_inv = 1.0 / sr;
|
||||
let sg_inv = 1.0 / sg;
|
||||
let sb_inv = 1.0 / sb;
|
||||
let srp = sr * det_inv;
|
||||
let sgp = sg * det_inv;
|
||||
let sbp = sb * det_inv;
|
||||
let XYZ_from_local = [
|
||||
[srp * xr, sgp * xg, sbp * xb, 0.0],
|
||||
[srp * yr, sgp * yg, sbp * yb, 0.0],
|
||||
[srp * zr, sgp * zg, sbp * zb, 0.0],
|
||||
];
|
||||
let local_from_XYZ = [
|
||||
[srx * sr_inv, sry * sr_inv, srz * sr_inv, 0.0],
|
||||
[sgx * sg_inv, sgy * sg_inv, sgz * sg_inv, 0.0],
|
||||
[sbx * sb_inv, sby * sb_inv, sbz * sb_inv, 0.0],
|
||||
];
|
||||
(
|
||||
ColorMatrix::new(XYZ_from_local),
|
||||
ColorMatrix::new(local_from_XYZ),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,6 @@ use {
|
|||
head_management::{
|
||||
HeadManagers, HeadState, jay_head_manager_session_v1::handle_jay_head_manager_done,
|
||||
},
|
||||
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
||||
wl_output::{BlendSpace, OutputId, PersistentOutputState, WlOutputGlobal},
|
||||
wl_seat::{handle_position_hint_requests, handle_warp_mouse_to_focus},
|
||||
wl_surface::{
|
||||
|
|
@ -49,8 +48,7 @@ use {
|
|||
kbvm::KbvmContext,
|
||||
leaks,
|
||||
logger::Logger,
|
||||
output_schedule::OutputSchedule,
|
||||
portal::{self, PortalStartup},
|
||||
output_schedule::create_output_schedule,
|
||||
pr_caps::{PrCapsThread, pr_caps},
|
||||
scale::Scale,
|
||||
sighand::{self, SighandError},
|
||||
|
|
@ -78,7 +76,6 @@ use {
|
|||
rc_eq::RcEq,
|
||||
refcounted::RefCounted,
|
||||
run_toplevel::RunToplevel,
|
||||
static_text::StaticText,
|
||||
tri::Try,
|
||||
},
|
||||
version::VERSION,
|
||||
|
|
@ -86,11 +83,8 @@ use {
|
|||
wheel::{Wheel, WheelError},
|
||||
},
|
||||
ahash::AHashSet,
|
||||
clap::ValueEnum,
|
||||
forker::ForkerProxy,
|
||||
jay_config::{_private::DEFAULT_SEAT_NAME, logging::LogLevel as ConfigLogLevel},
|
||||
linearize::Linearize,
|
||||
log::LevelFilter,
|
||||
jay_config::protocol::DEFAULT_SEAT_NAME,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
env,
|
||||
|
|
@ -121,19 +115,10 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
|||
None
|
||||
};
|
||||
let forker = create_forker(reaper_pid);
|
||||
let portal = portal::run_from_compositor(global.log_level);
|
||||
enable_profiler();
|
||||
let logger = Logger::install_compositor(global.log_level);
|
||||
let portal = match portal {
|
||||
Ok(p) => Some(p),
|
||||
Err(e) => {
|
||||
log::error!("Could not spawn portal: {}", ErrorFmt(e));
|
||||
None
|
||||
}
|
||||
};
|
||||
let res = start_compositor2(
|
||||
Some(forker),
|
||||
portal,
|
||||
Some(logger.clone()),
|
||||
args,
|
||||
None,
|
||||
|
|
@ -152,7 +137,7 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
|||
|
||||
#[cfg(feature = "it")]
|
||||
pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> {
|
||||
let res = start_compositor2(None, None, None, RunArgs::default(), Some(future), None);
|
||||
let res = start_compositor2(None, None, RunArgs::default(), Some(future), None);
|
||||
leaks::log_leaked();
|
||||
res
|
||||
}
|
||||
|
|
@ -194,7 +179,6 @@ pub type TestFuture = Box<dyn Fn(&Rc<State>) -> Box<dyn Future<Output = ()>>>;
|
|||
|
||||
fn start_compositor2(
|
||||
forker: Option<Rc<ForkerProxy>>,
|
||||
portal: Option<PortalStartup>,
|
||||
logger: Option<Arc<Logger>>,
|
||||
run_args: RunArgs,
|
||||
test_future: Option<TestFuture>,
|
||||
|
|
@ -262,8 +246,6 @@ fn start_compositor2(
|
|||
pending_output_render_data: Default::default(),
|
||||
pending_float_layout: Default::default(),
|
||||
pending_input_popup_positioning: Default::default(),
|
||||
pending_toplevel_screencasts: Default::default(),
|
||||
pending_screencast_reallocs_or_reconfigures: Default::default(),
|
||||
pending_placeholder_render_textures: Default::default(),
|
||||
pending_container_tab_render_textures: Default::default(),
|
||||
dbus: Dbus::new(&engine, &ring, &run_toplevel),
|
||||
|
|
@ -279,11 +261,14 @@ fn start_compositor2(
|
|||
change: Default::default(),
|
||||
timeout: Cell::new(Duration::from_secs(10 * 60)),
|
||||
grace_period: Cell::new(Duration::from_secs(5)),
|
||||
key_press_enables_dpms: Cell::new(false),
|
||||
mouse_move_enables_dpms: Cell::new(false),
|
||||
timeout_changed: Default::default(),
|
||||
inhibitors: Default::default(),
|
||||
inhibitors_changed: Default::default(),
|
||||
inhibited_idle_notifications: Default::default(),
|
||||
backend_idle: Cell::new(true),
|
||||
dpms_off_by_command: Cell::new(false),
|
||||
in_grace_period: Cell::new(false),
|
||||
},
|
||||
run_args,
|
||||
|
|
@ -301,11 +286,11 @@ fn start_compositor2(
|
|||
display: Default::default(),
|
||||
},
|
||||
acceptor: Default::default(),
|
||||
tagged_acceptors: Default::default(),
|
||||
serial: Default::default(),
|
||||
idle_inhibitor_ids: Default::default(),
|
||||
run_toplevel,
|
||||
config_dir: explicit_config_dir.or_else(config_dir),
|
||||
config_file_id: NumCell::new(1),
|
||||
tracker: Default::default(),
|
||||
data_offer_ids: Default::default(),
|
||||
data_source_ids: Default::default(),
|
||||
|
|
@ -338,8 +323,6 @@ fn start_compositor2(
|
|||
explicit_sync_supported: Default::default(),
|
||||
keyboard_state_ids: Default::default(),
|
||||
physical_keyboard_ids: Default::default(),
|
||||
security_context_acceptors: Default::default(),
|
||||
tagged_acceptors: Default::default(),
|
||||
cursor_user_group_ids: Default::default(),
|
||||
cursor_user_ids: Default::default(),
|
||||
cursor_user_groups: Default::default(),
|
||||
|
|
@ -418,13 +401,6 @@ fn start_compositor2(
|
|||
forker.setenv(key.as_bytes(), val.as_bytes());
|
||||
}
|
||||
}
|
||||
let mut _portal = None;
|
||||
if let (Some(portal), Some(logger)) = (portal, &logger) {
|
||||
_portal = Some(engine.spawn(
|
||||
"portal",
|
||||
portal.spawn(engine.clone(), ring.clone(), logger.clone()),
|
||||
));
|
||||
}
|
||||
let _compositor = engine.spawn("compositor", start_compositor3(state.clone(), test_future));
|
||||
ring.run()?;
|
||||
state.clear();
|
||||
|
|
@ -485,14 +461,7 @@ fn load_config(
|
|||
if for_test {
|
||||
return ConfigProxy::for_test(state);
|
||||
}
|
||||
match ConfigProxy::from_config_dir(state) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::warn!("Could not load config.so: {}", ErrorFmt(e));
|
||||
log::warn!("Using default config");
|
||||
ConfigProxy::default(state)
|
||||
}
|
||||
}
|
||||
ConfigProxy::default(state)
|
||||
}
|
||||
|
||||
fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||
|
|
@ -544,16 +513,6 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
|||
Phase::PostLayout,
|
||||
input_popup_positioning(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"toplevel screencast present",
|
||||
Phase::Present,
|
||||
perform_toplevel_screencasts(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"screencast realloc",
|
||||
Phase::PostLayout,
|
||||
perform_screencast_realloc(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"visualize damage",
|
||||
Phase::PostLayout,
|
||||
|
|
@ -742,7 +701,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
head_managers: HeadManagers::new(head_name, head_state),
|
||||
wlr_output_heads: Default::default(),
|
||||
});
|
||||
let schedule = Rc::new(OutputSchedule::new(
|
||||
let schedule = Rc::new(create_output_schedule(
|
||||
state,
|
||||
&connector_data,
|
||||
&persistent_state,
|
||||
|
|
@ -789,7 +748,6 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
lock_surface: Default::default(),
|
||||
hardware_cursor: Default::default(),
|
||||
update_render_data_scheduled: Cell::new(false),
|
||||
screencasts: Default::default(),
|
||||
hardware_cursor_needs_render: Cell::new(false),
|
||||
screencopies: Default::default(),
|
||||
title_visible: Cell::new(false),
|
||||
|
|
@ -830,65 +788,3 @@ pub fn config_dir() -> Option<String> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Hash, Default, Eq, PartialEq, Linearize)]
|
||||
pub enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
#[default]
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Off,
|
||||
}
|
||||
|
||||
impl Into<LevelFilter> for LogLevel {
|
||||
fn into(self) -> LevelFilter {
|
||||
match self {
|
||||
LogLevel::Trace => LevelFilter::Trace,
|
||||
LogLevel::Debug => LevelFilter::Debug,
|
||||
LogLevel::Info => LevelFilter::Info,
|
||||
LogLevel::Warn => LevelFilter::Warn,
|
||||
LogLevel::Error => LevelFilter::Error,
|
||||
LogLevel::Off => LevelFilter::Off,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LevelFilter> for LogLevel {
|
||||
fn from(value: LevelFilter) -> Self {
|
||||
match value {
|
||||
LevelFilter::Trace => LogLevel::Trace,
|
||||
LevelFilter::Debug => LogLevel::Debug,
|
||||
LevelFilter::Info => LogLevel::Info,
|
||||
LevelFilter::Warn => LogLevel::Warn,
|
||||
LevelFilter::Error => LogLevel::Error,
|
||||
LevelFilter::Off => LogLevel::Off,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticText for LogLevel {
|
||||
fn text(&self) -> &'static str {
|
||||
match self {
|
||||
LogLevel::Off => "Off",
|
||||
LogLevel::Error => "Error",
|
||||
LogLevel::Warn => "Warn",
|
||||
LogLevel::Info => "Info",
|
||||
LogLevel::Debug => "Debug",
|
||||
LogLevel::Trace => "Trace",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigLogLevel> for LogLevel {
|
||||
fn from(value: ConfigLogLevel) -> Self {
|
||||
match value {
|
||||
ConfigLogLevel::Trace => LogLevel::Trace,
|
||||
ConfigLogLevel::Debug => LogLevel::Debug,
|
||||
ConfigLogLevel::Info => LogLevel::Info,
|
||||
ConfigLogLevel::Warn => LogLevel::Warn,
|
||||
ConfigLogLevel::Error => LogLevel::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
161
src/config.rs
161
src/config.rs
|
|
@ -5,52 +5,29 @@ use crate::it::test_config::TEST_CONFIG_ENTRY;
|
|||
use {
|
||||
crate::{
|
||||
backend::{ConnectorId, DrmDeviceId, InputDeviceId},
|
||||
client::{Client, ClientCaps},
|
||||
config::handler::ConfigProxyHandler,
|
||||
ifs::wl_seat::SeatId,
|
||||
state::State,
|
||||
tree::{TileState, ToplevelData, ToplevelIdentifier},
|
||||
utils::{
|
||||
clonecell::CloneCell,
|
||||
nice::{JAY_NO_REALTIME, dont_allow_config_so},
|
||||
numcell::NumCell,
|
||||
ptr_ext::PtrExt,
|
||||
unlink_on_drop::UnlinkOnDrop,
|
||||
xrd::xrd,
|
||||
},
|
||||
},
|
||||
bincode::Options,
|
||||
jay_config::{
|
||||
_private::{
|
||||
ConfigEntry, VERSION, bincode_ops,
|
||||
ipc::{InitMessage, ServerFeature, ServerMessage, V1InitMessage},
|
||||
protocol::{
|
||||
ClientMessage, ConfigEntry, InitMessage, ServerFeature, ServerHandler, ServerMessage,
|
||||
Unref, V1InitMessage, VERSION, handle_client_message, init_client, unref_client,
|
||||
},
|
||||
input::{InputDevice, Seat, SwitchEvent},
|
||||
keyboard::{mods::Modifiers, syms::KeySym},
|
||||
video::{Connector, DrmDevice},
|
||||
window::{self},
|
||||
},
|
||||
libloading::Library,
|
||||
std::{cell::Cell, io, mem, path::Path, ptr, rc::Rc},
|
||||
thiserror::Error,
|
||||
std::{cell::Cell, mem, ptr, rc::Rc},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("Could not load the config library")]
|
||||
CouldNotLoadLibrary(#[source] libloading::Error),
|
||||
#[error("Config library does not contain the entry symbol")]
|
||||
LibraryDoesNotContainEntry(#[source] libloading::Error),
|
||||
#[error("Could not determine the config directory")]
|
||||
ConfigDirNotSet,
|
||||
#[error("Could not copy the config file")]
|
||||
CopyConfigFile(#[source] io::Error),
|
||||
#[error("XDG_RUNTIME_DIR is not set")]
|
||||
XrdNotSet,
|
||||
#[error("Custom config.so is not permitted")]
|
||||
NotPermitted,
|
||||
}
|
||||
|
||||
pub struct ConfigProxy {
|
||||
handler: CloneCell<Option<Rc<ConfigProxyHandler>>>,
|
||||
}
|
||||
|
|
@ -181,16 +158,6 @@ impl ConfigProxy {
|
|||
self.handler.get()?.initial_tile_state(data)
|
||||
}
|
||||
|
||||
pub fn update_capabilities(
|
||||
&self,
|
||||
data: &Rc<Client>,
|
||||
bounding_caps: ClientCaps,
|
||||
set_bounding_caps: bool,
|
||||
) {
|
||||
if let Some(handler) = self.handler.get() {
|
||||
handler.update_capabilities(data, bounding_caps, set_bounding_caps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ConfigProxy {
|
||||
|
|
@ -199,41 +166,32 @@ impl Drop for ConfigProxy {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn default_client_init(
|
||||
unsafe fn default_client_init(
|
||||
srv_data: *const u8,
|
||||
srv_unref: unsafe extern "C" fn(data: *const u8),
|
||||
srv_handler: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
|
||||
msg: *const u8,
|
||||
size: usize,
|
||||
srv_unref: Unref,
|
||||
srv_handler: ServerHandler,
|
||||
msg: InitMessage,
|
||||
) -> *const u8 {
|
||||
extern "C" fn configure() {
|
||||
fn configure() {
|
||||
jay_toml_config::configure();
|
||||
}
|
||||
unsafe {
|
||||
jay_config::_private::client::init(srv_data, srv_unref, srv_handler, msg, size, configure)
|
||||
init_client(srv_data, srv_unref, srv_handler, msg, configure)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigProxy {
|
||||
fn new(
|
||||
lib: Option<Library>,
|
||||
entry: &ConfigEntry,
|
||||
state: &Rc<State>,
|
||||
path: Option<String>,
|
||||
) -> Self {
|
||||
fn new(entry: &ConfigEntry, state: &Rc<State>) -> Self {
|
||||
let version = entry.version.min(VERSION);
|
||||
let data = Rc::new(ConfigProxyHandler {
|
||||
path,
|
||||
client_data: Cell::new(ptr::null()),
|
||||
dropped: Cell::new(false),
|
||||
_lib: lib,
|
||||
_version: version,
|
||||
unref: entry.unref,
|
||||
handle_msg: entry.handle_msg,
|
||||
state: state.clone(),
|
||||
next_id: NumCell::new(1),
|
||||
keymaps: Default::default(),
|
||||
bufs: Default::default(),
|
||||
workspace_ids: NumCell::new(1),
|
||||
workspaces_by_name: Default::default(),
|
||||
workspaces_by_id: Default::default(),
|
||||
|
|
@ -249,8 +207,6 @@ impl ConfigProxy {
|
|||
client_matchers: Default::default(),
|
||||
client_matcher_cache: Default::default(),
|
||||
client_matcher_leafs: Default::default(),
|
||||
client_matcher_capabilities: Default::default(),
|
||||
client_matcher_bounding_capabilities: Default::default(),
|
||||
window_matcher_ids: NumCell::new(1),
|
||||
window_matchers: Default::default(),
|
||||
window_matcher_cache: Default::default(),
|
||||
|
|
@ -259,16 +215,13 @@ impl ConfigProxy {
|
|||
window_matcher_no_auto_focus: Default::default(),
|
||||
window_matcher_initial_tile_state: Default::default(),
|
||||
});
|
||||
let init_msg = bincode_ops()
|
||||
.serialize(&InitMessage::V1(V1InitMessage {}))
|
||||
.unwrap();
|
||||
let init_msg = InitMessage::V1(V1InitMessage {});
|
||||
unsafe {
|
||||
let client_data = (entry.init)(
|
||||
Rc::into_raw(data.clone()) as _,
|
||||
unref,
|
||||
handle_msg,
|
||||
init_msg.as_ptr(),
|
||||
init_msg.len(),
|
||||
init_msg,
|
||||
);
|
||||
data.client_data.set(client_data);
|
||||
}
|
||||
|
|
@ -288,96 +241,32 @@ impl ConfigProxy {
|
|||
let entry = ConfigEntry {
|
||||
version: VERSION,
|
||||
init: default_client_init,
|
||||
unref: jay_config::_private::client::unref,
|
||||
handle_msg: jay_config::_private::client::handle_msg,
|
||||
unref: unref_client,
|
||||
handle_msg: handle_client_message,
|
||||
};
|
||||
Self::new(None, &entry, state, None)
|
||||
Self::new(&entry, state)
|
||||
}
|
||||
|
||||
#[cfg(feature = "it")]
|
||||
pub fn for_test(state: &Rc<State>) -> Self {
|
||||
Self::new(None, &TEST_CONFIG_ENTRY, state, None)
|
||||
}
|
||||
|
||||
pub fn from_config_dir(state: &Rc<State>) -> Result<Self, ConfigError> {
|
||||
if dont_allow_config_so() {
|
||||
if have_config_so(state.config_dir.as_deref()) {
|
||||
log::warn!("Not loading config.so because");
|
||||
log::warn!(" 1. Jay was started with CAP_SYS_NICE");
|
||||
log::warn!(" 2. Jay was not started with {}=1", JAY_NO_REALTIME);
|
||||
log::warn!(" 3. The scheduler was elevated to SCHED_RR");
|
||||
log::warn!(
|
||||
" 4. Jay was not compiled with {}=1",
|
||||
jay_allow_realtime_config_so!(),
|
||||
);
|
||||
}
|
||||
return Err(ConfigError::NotPermitted);
|
||||
}
|
||||
let dir = match state.config_dir.as_deref() {
|
||||
Some(d) => d,
|
||||
_ => return Err(ConfigError::ConfigDirNotSet),
|
||||
};
|
||||
let file = format!("{}/{CONFIG_SO}", dir);
|
||||
unsafe { Self::from_file(&file, state) }
|
||||
}
|
||||
|
||||
pub unsafe fn from_file(path: &str, state: &Rc<State>) -> Result<Self, ConfigError> {
|
||||
// Here we have to do a bit of a dance to support reloading. glibc will
|
||||
// never load a library twice unless it has been unloaded in between.
|
||||
// glibc identifies libraries by their file path and by their inode
|
||||
// number. If either of those match, glibc considers the libraries
|
||||
// identical. If the inode has not changed then this is not a problem
|
||||
// for us since we don't want glibc to do any unnecessary work.
|
||||
// However, if the user has created a new config with a new inode, then
|
||||
// glibc will still not reload the library if we try to load it from
|
||||
// the canonical location ~/.config/jay/config.so since it already has
|
||||
// a library with that path loaded. To work around this, create a
|
||||
// temporary copy with an incrementing number and load the library
|
||||
// from there.
|
||||
let xrd = match xrd() {
|
||||
Some(x) => x,
|
||||
_ => return Err(ConfigError::XrdNotSet),
|
||||
};
|
||||
let copy = format!(
|
||||
"{}/.jay_config.so.{}.{}",
|
||||
xrd,
|
||||
uapi::getpid(),
|
||||
state.config_file_id.fetch_add(1)
|
||||
);
|
||||
let _ = uapi::unlink(copy.as_str());
|
||||
if let Err(e) = std::fs::copy(path, ©) {
|
||||
return Err(ConfigError::CopyConfigFile(e));
|
||||
}
|
||||
let unlink = UnlinkOnDrop(©);
|
||||
let lib = match unsafe { Library::new(©) } {
|
||||
Ok(l) => l,
|
||||
Err(e) => return Err(ConfigError::CouldNotLoadLibrary(e)),
|
||||
};
|
||||
let entry = unsafe { lib.get::<&'static ConfigEntry>(b"JAY_CONFIG_ENTRY_V1\0") };
|
||||
let entry = match entry {
|
||||
Ok(e) => *e,
|
||||
Err(e) => return Err(ConfigError::LibraryDoesNotContainEntry(e)),
|
||||
};
|
||||
mem::forget(unlink);
|
||||
Ok(Self::new(Some(lib), entry, state, Some(copy)))
|
||||
Self::new(&TEST_CONFIG_ENTRY, state)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn unref(data: *const u8) {
|
||||
unsafe fn unref(data: *const u8) {
|
||||
let server = data as *const ConfigProxyHandler;
|
||||
unsafe {
|
||||
drop(Rc::from_raw(server));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn handle_msg(data: *const u8, msg: *const u8, size: usize) {
|
||||
unsafe fn handle_msg(data: *const u8, msg: &ClientMessage<'_>) {
|
||||
unsafe {
|
||||
let server = (data as *const ConfigProxyHandler).deref();
|
||||
if server.dropped.get() {
|
||||
return;
|
||||
}
|
||||
let rc = Rc::from_raw(server);
|
||||
let msg = std::slice::from_raw_parts(msg, size);
|
||||
rc.handle_request(msg);
|
||||
mem::forget(rc);
|
||||
}
|
||||
|
|
@ -388,15 +277,3 @@ pub struct InvokedShortcut {
|
|||
pub effective_mods: Modifiers,
|
||||
pub sym: KeySym,
|
||||
}
|
||||
|
||||
const CONFIG_SO: &str = "config.so";
|
||||
|
||||
pub fn have_config_so(config_dir: Option<&str>) -> bool {
|
||||
let Some(dir) = config_dir else {
|
||||
return false;
|
||||
};
|
||||
let mut dir = dir.to_owned();
|
||||
dir.push_str("/");
|
||||
dir.push_str(CONFIG_SO);
|
||||
Path::new(&dir).exists()
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
747
src/config/handler/dispatch.rs
Normal file
747
src/config/handler/dispatch.rs
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub fn handle_request(self: &Rc<Self>, msg: &ClientMessage<'_>) {
|
||||
if let Err(e) = self.handle_request_(msg) {
|
||||
log::error!("Could not handle client request: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request_(self: &Rc<Self>, request: &ClientMessage<'_>) -> Result<(), CphError> {
|
||||
let request = request.clone();
|
||||
match request {
|
||||
ClientMessage::Log {
|
||||
level,
|
||||
msg,
|
||||
file,
|
||||
line,
|
||||
} => self.handle_log_request(level, msg, file, line),
|
||||
ClientMessage::GetSeat { name } => self.handle_get_seat(name),
|
||||
ClientMessage::ParseKeymap { keymap } => {
|
||||
self.handle_parse_keymap(keymap).wrn("parse_keymap")?
|
||||
}
|
||||
ClientMessage::SeatSetKeymap { seat, keymap } => {
|
||||
self.handle_set_keymap(seat, keymap).wrn("set_keymap")?
|
||||
}
|
||||
ClientMessage::SeatGetRepeatRate { seat } => {
|
||||
self.handle_get_repeat_rate(seat).wrn("get_repeat_rate")?
|
||||
}
|
||||
ClientMessage::SeatSetRepeatRate { seat, rate, delay } => self
|
||||
.handle_set_repeat_rate(seat, rate, delay)
|
||||
.wrn("set_repeat_rate")?,
|
||||
ClientMessage::SetSeat { device, seat } => {
|
||||
self.handle_set_seat(device, seat).wrn("set_seat")?
|
||||
}
|
||||
ClientMessage::GetSeatMono { seat } => {
|
||||
self.handle_get_seat_mono(seat).wrn("get_seat_mono")?
|
||||
}
|
||||
ClientMessage::SetSeatMono { seat, mono } => {
|
||||
self.handle_set_seat_mono(seat, mono).wrn("set_seat_mono")?
|
||||
}
|
||||
ClientMessage::GetSeatSplit { seat } => {
|
||||
self.handle_get_seat_split(seat).wrn("get_seat_split")?
|
||||
}
|
||||
ClientMessage::SetSeatSplit { seat, axis } => self
|
||||
.handle_set_seat_split(seat, axis)
|
||||
.wrn("set_seat_split")?,
|
||||
ClientMessage::AddShortcut { seat, mods, sym } => self
|
||||
.handle_add_shortcut(seat, Modifiers(!0), mods, sym)
|
||||
.wrn("add_shortcut")?,
|
||||
ClientMessage::RemoveShortcut { seat, mods, sym } => self
|
||||
.handle_remove_shortcut(seat, mods, sym)
|
||||
.wrn("remove_shortcut")?,
|
||||
ClientMessage::SeatFocus { seat, direction } => {
|
||||
self.handle_seat_focus(seat, direction).wrn("seat_focus")?
|
||||
}
|
||||
ClientMessage::SeatMove { seat, direction } => {
|
||||
self.handle_seat_move(seat, direction).wrn("seat_move")?
|
||||
}
|
||||
ClientMessage::GetInputDevices { seat } => self.handle_get_input_devices(seat),
|
||||
ClientMessage::GetSeats => self.handle_get_seats(),
|
||||
ClientMessage::RemoveSeat { .. } => {}
|
||||
ClientMessage::Run { prog, args, env } => {
|
||||
self.handle_run(prog, args, env, vec![], None).wrn("run")?
|
||||
}
|
||||
ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab).wrn("grab")?,
|
||||
ClientMessage::SetColor { colorable, color } => {
|
||||
self.handle_set_color(colorable, color).wrn("set_color")?
|
||||
}
|
||||
ClientMessage::GetColor { colorable } => {
|
||||
self.handle_get_color(colorable).wrn("get_color")?
|
||||
}
|
||||
ClientMessage::CreateSeatSplit { seat, axis } => self
|
||||
.handle_create_seat_split(seat, axis)
|
||||
.wrn("create_seat_split")?,
|
||||
ClientMessage::FocusSeatParent { seat } => self
|
||||
.handle_focus_seat_parent(seat)
|
||||
.wrn("focus_seat_parent")?,
|
||||
ClientMessage::GetSeatFloating { seat } => self
|
||||
.handle_get_seat_floating(seat)
|
||||
.wrn("get_seat_floating")?,
|
||||
ClientMessage::SetSeatFloating { seat, floating } => self
|
||||
.handle_set_seat_floating(seat, floating)
|
||||
.wrn("set_seat_floating")?,
|
||||
ClientMessage::Quit => self.handle_quit(),
|
||||
ClientMessage::SwitchTo { vtnr } => self.handle_switch_to(vtnr),
|
||||
ClientMessage::HasCapability { device, cap } => self
|
||||
.handle_has_capability(device, cap)
|
||||
.wrn("has_capability")?,
|
||||
ClientMessage::SetLeftHanded {
|
||||
device,
|
||||
left_handed,
|
||||
} => self
|
||||
.handle_set_left_handed(device, left_handed)
|
||||
.wrn("set_left_handed")?,
|
||||
ClientMessage::SetAccelProfile { device, profile } => self
|
||||
.handle_set_accel_profile(device, profile)
|
||||
.wrn("set_accel_profile")?,
|
||||
ClientMessage::SetAccelSpeed { device, speed } => self
|
||||
.handle_set_accel_speed(device, speed)
|
||||
.wrn("set_accel_speed")?,
|
||||
ClientMessage::SetTransformMatrix { device, matrix } => self
|
||||
.handle_set_transform_matrix(device, matrix)
|
||||
.wrn("set_transform_matrix")?,
|
||||
ClientMessage::GetDeviceName { device } => {
|
||||
self.handle_get_device_name(device).wrn("get_device_name")?
|
||||
}
|
||||
ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name),
|
||||
ClientMessage::ShowWorkspace { seat, workspace } => self
|
||||
.handle_show_workspace(seat, workspace, None)
|
||||
.wrn("show_workspace")?,
|
||||
ClientMessage::SetSeatWorkspace { seat, workspace } => self
|
||||
.handle_set_seat_workspace(seat, workspace)
|
||||
.wrn("set_seat_workspace")?,
|
||||
ClientMessage::SeatSendToScratchpad { seat, name } => self
|
||||
.handle_seat_send_to_scratchpad(seat, name)
|
||||
.wrn("seat_send_to_scratchpad")?,
|
||||
ClientMessage::SeatToggleScratchpad { seat, name } => self
|
||||
.handle_seat_toggle_scratchpad(seat, name)
|
||||
.wrn("seat_toggle_scratchpad")?,
|
||||
ClientMessage::SeatCycleScratchpad { seat, name } => self
|
||||
.handle_seat_cycle_scratchpad(seat, name)
|
||||
.wrn("seat_cycle_scratchpad")?,
|
||||
ClientMessage::GetConnector { ty, idx } => {
|
||||
self.handle_get_connector(ty, idx).wrn("get_connector")?
|
||||
}
|
||||
ClientMessage::ConnectorConnected { connector } => self
|
||||
.handle_connector_connected(connector)
|
||||
.wrn("connector_connected")?,
|
||||
ClientMessage::ConnectorType { connector } => self
|
||||
.handle_connector_type(connector)
|
||||
.wrn("connector_type")?,
|
||||
ClientMessage::ConnectorMode { connector } => self
|
||||
.handle_connector_mode(connector)
|
||||
.wrn("connector_mode")?,
|
||||
ClientMessage::ConnectorSetPosition { connector, x, y } => self
|
||||
.handle_connector_set_position(connector, x, y)
|
||||
.wrn("connector_set_position")?,
|
||||
ClientMessage::ConnectorSetEnabled { connector, enabled } => self
|
||||
.handle_connector_set_enabled(connector, enabled)
|
||||
.wrn("connector_set_enabled")?,
|
||||
ClientMessage::SeatClose { seat } => self.handle_seat_close(seat).wrn("seat_close")?,
|
||||
ClientMessage::SetStatus { status } => self.handle_set_status(status),
|
||||
ClientMessage::GetTimer { name } => self.handle_get_timer(name).wrn("get_timer")?,
|
||||
ClientMessage::RemoveTimer { timer } => {
|
||||
self.handle_remove_timer(timer).wrn("remove_timer")?
|
||||
}
|
||||
ClientMessage::ProgramTimer {
|
||||
timer,
|
||||
initial,
|
||||
periodic,
|
||||
} => self
|
||||
.handle_program_timer(timer, initial, periodic)
|
||||
.wrn("program_timer")?,
|
||||
ClientMessage::SetEnv { key, val } => self.handle_set_env(key, val),
|
||||
ClientMessage::SetSeatFullscreen { seat, fullscreen } => self
|
||||
.handle_set_seat_fullscreen(seat, fullscreen)
|
||||
.wrn("set_seat_fullscreen")?,
|
||||
ClientMessage::GetSeatFullscreen { seat } => self
|
||||
.handle_get_seat_fullscreen(seat)
|
||||
.wrn("get_seat_fullscreen")?,
|
||||
ClientMessage::Reload => self.handle_reload(),
|
||||
ClientMessage::GetDeviceConnectors { device } => self
|
||||
.handle_get_connectors(Some(device), false)
|
||||
.wrn("get_device_connectors")?,
|
||||
ClientMessage::GetDrmDeviceSyspath { device } => self
|
||||
.handle_get_drm_device_syspath(device)
|
||||
.wrn("get_drm_device_syspath")?,
|
||||
ClientMessage::GetDrmDeviceVendor { device } => self
|
||||
.handle_get_drm_device_vendor(device)
|
||||
.wrn("get_drm_device_vendor")?,
|
||||
ClientMessage::GetDrmDeviceModel { device } => self
|
||||
.handle_get_drm_device_model(device)
|
||||
.wrn("get_drm_device_model")?,
|
||||
ClientMessage::GetDrmDevices => self.handle_get_drm_devices(),
|
||||
ClientMessage::GetDrmDevicePciId { device } => self
|
||||
.handle_get_drm_device_pci_id(device)
|
||||
.wrn("get_drm_device_pci_id")?,
|
||||
ClientMessage::ResetColors => self.handle_reset_colors(),
|
||||
ClientMessage::ResetSizes => self.handle_reset_sizes(),
|
||||
ClientMessage::GetSize { sized } => self.handle_get_size(sized).wrn("get_size")?,
|
||||
ClientMessage::SetSize { sized, size } => {
|
||||
self.handle_set_size(sized, size).wrn("set_size")?
|
||||
}
|
||||
ClientMessage::ResetFont => self.handle_reset_font(),
|
||||
ClientMessage::GetFont => self.handle_get_font(),
|
||||
ClientMessage::SetFont { font } => self.handle_set_font(font),
|
||||
ClientMessage::SetPxPerWheelScroll { device, px } => self
|
||||
.handle_set_px_per_wheel_scroll(device, px)
|
||||
.wrn("set_px_per_wheel_scroll")?,
|
||||
ClientMessage::ConnectorSetScale { connector, scale } => self
|
||||
.handle_connector_set_scale(connector, scale)
|
||||
.wrn("connector_set_scale")?,
|
||||
ClientMessage::ConnectorGetScale { connector } => self
|
||||
.handle_connector_get_scale(connector)
|
||||
.wrn("connector_get_scale")?,
|
||||
ClientMessage::ConnectorSize { connector } => self
|
||||
.handle_connector_size(connector)
|
||||
.wrn("connector_size")?,
|
||||
ClientMessage::SetCursorSize { seat, size } => self
|
||||
.handle_set_cursor_size(seat, size)
|
||||
.wrn("set_cursor_size")?,
|
||||
ClientMessage::SetTapEnabled { device, enabled } => self
|
||||
.handle_set_tap_enabled(device, enabled)
|
||||
.wrn("set_tap_enabled")?,
|
||||
ClientMessage::SetDragEnabled { device, enabled } => self
|
||||
.handle_set_drag_enabled(device, enabled)
|
||||
.wrn("set_drag_enabled")?,
|
||||
ClientMessage::SetDragLockEnabled { device, enabled } => self
|
||||
.handle_set_drag_lock_enabled(device, enabled)
|
||||
.wrn("set_drag_lock_enabled")?,
|
||||
ClientMessage::SetUseHardwareCursor {
|
||||
seat,
|
||||
use_hardware_cursor,
|
||||
} => self
|
||||
.handle_set_use_hardware_cursor(seat, use_hardware_cursor)
|
||||
.wrn("set_use_hardware_cursor")?,
|
||||
ClientMessage::DisablePointerConstraint { seat } => self
|
||||
.handle_disable_pointer_constraint(seat)
|
||||
.wrn("disable_pointer_constraint")?,
|
||||
ClientMessage::MakeRenderDevice { device } => self
|
||||
.handle_make_render_device(device)
|
||||
.wrn("make_render_device")?,
|
||||
ClientMessage::GetSeatCursorWorkspace { seat } => self
|
||||
.handle_get_seat_cursor_workspace(seat)
|
||||
.wrn("get_seat_cursor_workspace")?,
|
||||
ClientMessage::GetSeatKeyboardWorkspace { seat } => self
|
||||
.handle_get_seat_keyboard_workspace(seat)
|
||||
.wrn("get_seat_keyboard_workspace")?,
|
||||
ClientMessage::SetDefaultWorkspaceCapture { capture } => {
|
||||
self.handle_set_default_workspace_capture(capture)
|
||||
}
|
||||
ClientMessage::GetDefaultWorkspaceCapture => {
|
||||
self.handle_get_default_workspace_capture()
|
||||
}
|
||||
ClientMessage::SetWorkspaceCapture { workspace, capture } => self
|
||||
.handle_set_workspace_capture(workspace, capture)
|
||||
.wrn("set_workspace_capture")?,
|
||||
ClientMessage::GetWorkspaceCapture { workspace } => self
|
||||
.handle_get_workspace_capture(workspace)
|
||||
.wrn("get_workspace_capture")?,
|
||||
ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self
|
||||
.handle_set_natural_scrolling_enabled(device, enabled)
|
||||
.wrn("set_natural_scrolling_enabled")?,
|
||||
ClientMessage::SetGfxApi { device, api } => {
|
||||
self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?
|
||||
}
|
||||
ClientMessage::SetDirectScanoutEnabled { device, enabled } => self
|
||||
.handle_set_direct_scanout_enabled(device, enabled)
|
||||
.wrn("set_direct_scanout_enabled")?,
|
||||
ClientMessage::ConnectorSetTransform {
|
||||
connector,
|
||||
transform,
|
||||
} => self
|
||||
.handle_connector_set_transform(connector, transform)
|
||||
.wrn("connector_set_transform")?,
|
||||
ClientMessage::SetDoubleClickIntervalUsec { usec } => {
|
||||
self.handle_set_double_click_interval_usec(usec)
|
||||
}
|
||||
ClientMessage::SetDoubleClickDistance { dist } => {
|
||||
self.handle_set_double_click_distance(dist)
|
||||
}
|
||||
ClientMessage::ConnectorModes { connector } => self
|
||||
.handle_connector_modes(connector)
|
||||
.wrn("connector_modes")?,
|
||||
ClientMessage::ConnectorSetMode { connector, mode } => self
|
||||
.handle_connector_set_mode(connector, mode)
|
||||
.wrn("connector_set_mode")?,
|
||||
ClientMessage::AddPollable { fd } => {
|
||||
self.handle_add_pollable(fd).wrn("add_pollable")?
|
||||
}
|
||||
ClientMessage::RemovePollable { id } => self.handle_remove_pollable(id),
|
||||
ClientMessage::AddInterest { pollable, writable } => self
|
||||
.handle_add_interest(pollable, writable)
|
||||
.wrn("add_interest")?,
|
||||
ClientMessage::Run2 {
|
||||
prog,
|
||||
args,
|
||||
env,
|
||||
fds,
|
||||
} => self.handle_run(prog, args, env, fds, None).wrn("run")?,
|
||||
ClientMessage::Run3 {
|
||||
prog,
|
||||
args,
|
||||
env,
|
||||
fds,
|
||||
tag,
|
||||
} => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
|
||||
ClientMessage::DisableDefaultSeat => self.state.create_default_seat.set(false),
|
||||
ClientMessage::DestroyKeymap { keymap } => self.handle_destroy_keymap(keymap),
|
||||
ClientMessage::GetConnectorName { connector } => self
|
||||
.handle_connector_name(connector)
|
||||
.wrn("connector_name")?,
|
||||
ClientMessage::GetConnectorModel { connector } => self
|
||||
.handle_connector_model(connector)
|
||||
.wrn("connector_model")?,
|
||||
ClientMessage::GetConnectorManufacturer { connector } => self
|
||||
.handle_connector_manufacturer(connector)
|
||||
.wrn("connector_manufacturer")?,
|
||||
ClientMessage::GetConnectorSerialNumber { connector } => self
|
||||
.handle_connector_serial_number(connector)
|
||||
.wrn("connector_serial_number")?,
|
||||
ClientMessage::GetConnectors {
|
||||
device,
|
||||
connected_only,
|
||||
} => self
|
||||
.handle_get_connectors(device, connected_only)
|
||||
.wrn("get_connectors")?,
|
||||
ClientMessage::ConnectorGetPosition { connector } => self
|
||||
.handle_connector_get_position(connector)
|
||||
.wrn("connector_get_position")?,
|
||||
ClientMessage::GetConfigDir => self.handle_get_config_dir(),
|
||||
ClientMessage::GetWorkspaces => self.handle_get_workspaces(),
|
||||
ClientMessage::UnsetEnv { key } => self.handle_unset_env(key),
|
||||
ClientMessage::SetLogLevel { level } => self.handle_set_log_level(level),
|
||||
ClientMessage::GetDrmDeviceDevnode { device } => self
|
||||
.handle_get_drm_device_devnode(device)
|
||||
.wrn("get_drm_device_devnode")?,
|
||||
ClientMessage::GetInputDeviceSyspath { device } => self
|
||||
.handle_get_input_device_syspath(device)
|
||||
.wrn("get_input_device_syspath")?,
|
||||
ClientMessage::GetInputDeviceDevnode { device } => self
|
||||
.handle_get_input_device_devnode(device)
|
||||
.wrn("get_input_device_devnode")?,
|
||||
ClientMessage::SetIdle { timeout } => self.handle_set_idle(timeout),
|
||||
ClientMessage::SetKeyPressEnablesDpms { enabled } => {
|
||||
self.handle_set_key_press_enables_dpms(enabled)
|
||||
}
|
||||
ClientMessage::SetMouseMoveEnablesDpms { enabled } => {
|
||||
self.handle_set_mouse_move_enables_dpms(enabled)
|
||||
}
|
||||
ClientMessage::MoveToOutput {
|
||||
workspace,
|
||||
connector,
|
||||
} => self
|
||||
.handle_move_to_output(workspace, connector)
|
||||
.wrn("move_to_output")?,
|
||||
ClientMessage::SetExplicitSyncEnabled { enabled } => {
|
||||
self.handle_set_explicit_sync_enabled(enabled)
|
||||
}
|
||||
ClientMessage::DeviceSetKeymap { device, keymap } => self
|
||||
.handle_set_device_keymap(device, keymap)
|
||||
.wrn("set_device_keymap")?,
|
||||
ClientMessage::SetForward { seat, forward } => {
|
||||
self.handle_set_forward(seat, forward).wrn("set_forward")?
|
||||
}
|
||||
ClientMessage::AddShortcut2 {
|
||||
seat,
|
||||
mod_mask,
|
||||
mods,
|
||||
sym,
|
||||
} => self
|
||||
.handle_add_shortcut(seat, mod_mask, mods, sym)
|
||||
.wrn("add_shortcut")?,
|
||||
ClientMessage::SetFocusFollowsMouseMode { seat, mode } => self
|
||||
.handle_set_focus_follows_mouse_mode(seat, mode)
|
||||
.wrn("set_focus_follows_mouse_mode")?,
|
||||
ClientMessage::SetInputDeviceConnector {
|
||||
input_device,
|
||||
connector,
|
||||
} => self
|
||||
.handle_set_input_device_connector(input_device, connector)
|
||||
.wrn("set_input_device_connector")?,
|
||||
ClientMessage::RemoveInputMapping { input_device } => self
|
||||
.handle_remove_input_mapping(input_device)
|
||||
.wrn("remove_input_mapping")?,
|
||||
ClientMessage::SetWindowManagementEnabled { seat, enabled } => self
|
||||
.handle_set_window_management_enabled(seat, enabled)
|
||||
.wrn("set_window_management_enabled")?,
|
||||
ClientMessage::SetVrrMode { connector, mode } => self
|
||||
.handle_set_vrr_mode(connector, mode)
|
||||
.wrn("set_vrr_mode")?,
|
||||
ClientMessage::SetVrrCursorHz { connector, hz } => self
|
||||
.handle_set_vrr_cursor_hz(connector, hz)
|
||||
.wrn("set_vrr_cursor_hz")?,
|
||||
ClientMessage::SetTearingMode { connector, mode } => self
|
||||
.handle_set_tearing_mode(connector, mode)
|
||||
.wrn("set_tearing_mode")?,
|
||||
ClientMessage::SetCalibrationMatrix { device, matrix } => self
|
||||
.handle_set_calibration_matrix(device, matrix)
|
||||
.wrn("set_calibration_matrix")?,
|
||||
ClientMessage::SetEiSocketEnabled { enabled } => {
|
||||
self.handle_set_ei_socket_enabled(enabled)
|
||||
}
|
||||
ClientMessage::ConnectorSetFormat { connector, format } => self
|
||||
.handle_connector_set_format(connector, format)
|
||||
.wrn("connector_set_format")?,
|
||||
ClientMessage::SetFlipMargin { device, margin } => self
|
||||
.handle_set_flip_margin(device, margin)
|
||||
.wrn("set_flip_margin")?,
|
||||
ClientMessage::SetUiDragEnabled { enabled } => self.handle_set_ui_drag_enabled(enabled),
|
||||
ClientMessage::SetUiDragThreshold { threshold } => {
|
||||
self.handle_set_ui_drag_threshold(threshold)
|
||||
}
|
||||
ClientMessage::SetAnimationsEnabled { enabled } => {
|
||||
self.handle_set_animations_enabled(enabled)
|
||||
}
|
||||
ClientMessage::SetAnimationDurationMs { duration_ms } => {
|
||||
self.handle_set_animation_duration_ms(duration_ms)
|
||||
}
|
||||
ClientMessage::SetAnimationCurve { curve } => self.handle_set_animation_curve(curve),
|
||||
ClientMessage::SetAnimationStyle { style } => self.handle_set_animation_style(style),
|
||||
ClientMessage::SetAnimationCubicBezier { x1, y1, x2, y2 } => {
|
||||
self.handle_set_animation_cubic_bezier(x1, y1, x2, y2)
|
||||
}
|
||||
ClientMessage::SetXScalingMode { mode } => self
|
||||
.handle_set_x_scaling_mode(mode)
|
||||
.wrn("set_x_scaling_mode")?,
|
||||
ClientMessage::SetIdleGracePeriod { period } => {
|
||||
self.handle_set_idle_grace_period(period)
|
||||
}
|
||||
ClientMessage::SetColorManagementEnabled { enabled } => {
|
||||
self.handle_set_color_management_enabled(enabled)
|
||||
}
|
||||
ClientMessage::ConnectorSetColors {
|
||||
connector,
|
||||
color_space,
|
||||
eotf,
|
||||
} => self
|
||||
.handle_connector_set_colors(connector, color_space, eotf)
|
||||
.wrn("connector_set_colors")?,
|
||||
ClientMessage::ConnectorSetBrightness {
|
||||
connector,
|
||||
brightness,
|
||||
} => self
|
||||
.handle_connector_set_brightness(connector, brightness)
|
||||
.wrn("connector_set_brightness")?,
|
||||
ClientMessage::SetFloatAboveFullscreen { above } => {
|
||||
self.handle_set_float_above_fullscreen(above)
|
||||
}
|
||||
ClientMessage::GetFloatAboveFullscreen => self.handle_get_float_above_fullscreen(),
|
||||
ClientMessage::GetSeatFloatPinned { seat } => self
|
||||
.handle_get_seat_float_pinned(seat)
|
||||
.wrn("get_seat_float_pinned")?,
|
||||
ClientMessage::SetSeatFloatPinned { seat, pinned } => self
|
||||
.handle_set_seat_float_pinned(seat, pinned)
|
||||
.wrn("set_seat_float_pinned")?,
|
||||
ClientMessage::SetShowFloatPinIcon { show } => {
|
||||
self.handle_set_show_float_pin_icon(show)
|
||||
}
|
||||
ClientMessage::GetConnectorActiveWorkspace { connector } => self
|
||||
.handle_get_connector_active_workspace(connector)
|
||||
.wrn("get_connector_active_workspace")?,
|
||||
ClientMessage::GetConnectorWorkspaces { connector } => self
|
||||
.handle_get_connector_workspaces(connector)
|
||||
.wrn("get_connector_workspaces")?,
|
||||
ClientMessage::GetWorkspaceConnector { workspace } => self
|
||||
.handle_get_workspace_connector(workspace)
|
||||
.wrn("get_workspace_connector")?,
|
||||
ClientMessage::GetConnectorInDirection {
|
||||
connector,
|
||||
direction,
|
||||
} => self
|
||||
.handle_get_connector_in_direction(connector, direction)
|
||||
.wrn("get_connector_in_direction")?,
|
||||
ClientMessage::GetClients => self.handle_get_clients(),
|
||||
ClientMessage::ClientExists { client } => self.handle_client_exists(client),
|
||||
ClientMessage::ClientIsXwayland { client } => self
|
||||
.handle_client_is_xwayland(client)
|
||||
.wrn("client_is_xwayland")?,
|
||||
ClientMessage::ClientKill { client } => self.handle_client_kill(client),
|
||||
ClientMessage::WindowExists { window } => self.handle_window_exists(window),
|
||||
ClientMessage::GetWorkspaceWindow { workspace } => self
|
||||
.handle_get_workspace_window(workspace)
|
||||
.wrn("get_workspace_window")?,
|
||||
ClientMessage::GetSeatKeyboardWindow { seat } => self
|
||||
.handle_get_seat_keyboard_window(seat)
|
||||
.wrn("get_seat_keyboard_window")?,
|
||||
ClientMessage::SeatFocusWindow { seat, window } => self
|
||||
.handle_seat_focus_window(seat, window)
|
||||
.wrn("seat_focus_window")?,
|
||||
ClientMessage::GetWindowTitle { window } => self
|
||||
.handle_get_window_title(window)
|
||||
.wrn("get_window_title")?,
|
||||
ClientMessage::GetWindowType { window } => {
|
||||
self.handle_get_window_type(window).wrn("get_window_type")?
|
||||
}
|
||||
ClientMessage::GetWindowId { window } => {
|
||||
self.handle_get_window_id(window).wrn("get_window_id")?
|
||||
}
|
||||
ClientMessage::GetWindowParent { window } => self
|
||||
.handle_get_window_parent(window)
|
||||
.wrn("get_window_parent")?,
|
||||
ClientMessage::GetWindowWorkspace { window } => self
|
||||
.handle_get_window_workspace(window)
|
||||
.wrn("get_window_workspace")?,
|
||||
ClientMessage::GetWindowChildren { window } => self
|
||||
.handle_get_window_children(window)
|
||||
.wrn("get_window_children")?,
|
||||
ClientMessage::GetWindowSplit { window } => self
|
||||
.handle_get_window_split(window)
|
||||
.wrn("get_window_split")?,
|
||||
ClientMessage::SetWindowSplit { window, axis } => self
|
||||
.handle_set_window_split(window, axis)
|
||||
.wrn("set_window_split")?,
|
||||
ClientMessage::GetWindowMono { window } => {
|
||||
self.handle_get_window_mono(window).wrn("get_window_mono")?
|
||||
}
|
||||
ClientMessage::SetWindowMono { window, mono } => self
|
||||
.handle_set_window_mono(window, mono)
|
||||
.wrn("set_window_mono")?,
|
||||
ClientMessage::WindowMove { window, direction } => self
|
||||
.handle_window_move(window, direction)
|
||||
.wrn("window_move")?,
|
||||
ClientMessage::CreateWindowSplit { window, axis } => self
|
||||
.handle_create_window_split(window, axis)
|
||||
.wrn("create_window_split")?,
|
||||
ClientMessage::WindowClose { window } => {
|
||||
self.handle_window_close(window).wrn("close_window")?
|
||||
}
|
||||
ClientMessage::GetWindowFloating { window } => self
|
||||
.handle_get_window_floating(window)
|
||||
.wrn("get_window_floating")?,
|
||||
ClientMessage::SetWindowFloating { window, floating } => self
|
||||
.handle_set_window_floating(window, floating)
|
||||
.wrn("set_window_floating")?,
|
||||
ClientMessage::SetWindowWorkspace { window, workspace } => self
|
||||
.handle_set_window_workspace(window, workspace)
|
||||
.wrn("set_window_workspace")?,
|
||||
ClientMessage::WindowSendToScratchpad { window, name } => self
|
||||
.handle_window_send_to_scratchpad(window, name)
|
||||
.wrn("window_send_to_scratchpad")?,
|
||||
ClientMessage::SetWindowFullscreen { window, fullscreen } => self
|
||||
.handle_set_window_fullscreen(window, fullscreen)
|
||||
.wrn("set_window_fullscreen")?,
|
||||
ClientMessage::GetWindowFullscreen { window } => self
|
||||
.handle_get_window_fullscreen(window)
|
||||
.wrn("get_window_fullscreen")?,
|
||||
ClientMessage::GetWindowFloatPinned { window } => self
|
||||
.handle_get_window_float_pinned(window)
|
||||
.wrn("get_window_float_pinned")?,
|
||||
ClientMessage::SetWindowFloatPinned { window, pinned } => self
|
||||
.handle_set_window_float_pinned(window, pinned)
|
||||
.wrn("set_window_float_pinned")?,
|
||||
ClientMessage::GetWindowIsVisible { window } => self
|
||||
.handle_get_window_is_visible(window)
|
||||
.wrn("get_window_is_visible")?,
|
||||
ClientMessage::GetWindowClient { window } => self
|
||||
.handle_get_window_client(window)
|
||||
.wrn("get_window_client")?,
|
||||
ClientMessage::CreateClientMatcher { criterion } => self
|
||||
.handle_create_client_matcher(criterion)
|
||||
.wrn("create_window_matcher")?,
|
||||
ClientMessage::DestroyClientMatcher { matcher } => {
|
||||
self.handle_destroy_client_matcher(matcher)
|
||||
}
|
||||
ClientMessage::EnableClientMatcherEvents { matcher } => self
|
||||
.handle_enable_client_matcher_events(matcher)
|
||||
.wrn("enable_window_matcher_events")?,
|
||||
ClientMessage::CreateWindowMatcher { criterion } => self
|
||||
.handle_create_window_matcher(criterion)
|
||||
.wrn("create_window_matcher")?,
|
||||
ClientMessage::DestroyWindowMatcher { matcher } => {
|
||||
self.handle_destroy_window_matcher(matcher)
|
||||
}
|
||||
ClientMessage::EnableWindowMatcherEvents { matcher } => self
|
||||
.handle_enable_window_matcher_events(matcher)
|
||||
.wrn("enable_window_matcher_events")?,
|
||||
ClientMessage::SetWindowMatcherAutoFocus {
|
||||
matcher,
|
||||
auto_focus,
|
||||
} => self
|
||||
.handle_set_window_matcher_auto_focus(matcher, auto_focus)
|
||||
.wrn("set_window_matcher_auto_focus")?,
|
||||
ClientMessage::SetWindowMatcherInitialTileState {
|
||||
matcher,
|
||||
tile_state,
|
||||
} => self
|
||||
.handle_set_window_matcher_initial_tile_state(matcher, tile_state)
|
||||
.wrn("set_window_matcher_initial_tile_state")?,
|
||||
ClientMessage::SetPointerRevertKey { seat, key } => self
|
||||
.handle_set_pointer_revert_key(seat, key)
|
||||
.wrn("set_pointer_revert_key")?,
|
||||
ClientMessage::SetClickMethod { device, method } => self
|
||||
.handle_set_click_method(device, method)
|
||||
.wrn("set_click_method")?,
|
||||
ClientMessage::SetMiddleButtonEmulationEnabled { device, enabled } => self
|
||||
.handle_set_middle_button_emulation_enabled(device, enabled)
|
||||
.wrn("set_middle_button_emulation_enabled")?,
|
||||
ClientMessage::GetContentType { window } => self
|
||||
.handle_get_content_type(window)
|
||||
.wrn("get_content_type")?,
|
||||
ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
|
||||
ClientMessage::GetShowBar => self.handle_get_show_bar(),
|
||||
ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show),
|
||||
ClientMessage::GetShowTitles => self.handle_get_show_titles(),
|
||||
ClientMessage::SetFloatingTitles { floating } => {
|
||||
self.handle_set_floating_titles(floating)
|
||||
}
|
||||
ClientMessage::GetFloatingTitles => self.handle_get_floating_titles(),
|
||||
ClientMessage::SetBarPosition { position } => self
|
||||
.handle_set_bar_position(position)
|
||||
.wrn("set_bar_position")?,
|
||||
ClientMessage::GetBarPosition => self.handle_get_bar_position(),
|
||||
ClientMessage::SetCornerRadius { radius } => self.handle_set_corner_radius(radius),
|
||||
ClientMessage::GetCornerRadius => self.handle_get_corner_radius(),
|
||||
ClientMessage::SeatFocusHistory { seat, timeline } => self
|
||||
.handle_seat_focus_history(seat, timeline)
|
||||
.wrn("seat_focus_history")?,
|
||||
ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible } => self
|
||||
.handle_seat_focus_history_set_only_visible(seat, only_visible)
|
||||
.wrn("seat_focus_history_set_only_visible")?,
|
||||
ClientMessage::SeatFocusHistorySetSameWorkspace {
|
||||
seat,
|
||||
same_workspace,
|
||||
} => self
|
||||
.handle_seat_focus_history_set_same_workspace(seat, same_workspace)
|
||||
.wrn("seat_focus_history_set_same_workspace")?,
|
||||
ClientMessage::SeatFocusLayerRel { seat, direction } => self
|
||||
.handle_seat_focus_layer_rel(seat, direction)
|
||||
.wrn("seat_focus_layer_rel")?,
|
||||
ClientMessage::SeatFocusTiles { seat } => {
|
||||
self.handle_seat_focus_tiles(seat).wrn("seat_focus_tiles")?
|
||||
}
|
||||
ClientMessage::SeatFocusFloats { seat } => self
|
||||
.handle_seat_focus_floats(seat)
|
||||
.wrn("seat_focus_floats")?,
|
||||
ClientMessage::SeatToggleFocusFloatTiled { seat } => self
|
||||
.handle_seat_toggle_focus_float_tiled(seat)
|
||||
.wrn("seat_toggle_focus_float_tiled")?,
|
||||
ClientMessage::SetMiddleClickPasteEnabled { enabled } => {
|
||||
self.handle_set_middle_click_paste_enabled(enabled)
|
||||
}
|
||||
ClientMessage::SetWorkspaceDisplayOrder { order } => {
|
||||
self.handle_set_workspace_display_order(order)
|
||||
}
|
||||
ClientMessage::SeatCreateMark { seat, kc } => self
|
||||
.handle_seat_create_mark(seat, kc)
|
||||
.wrn("seat_create_mark")?,
|
||||
ClientMessage::SeatJumpToMark { seat, kc } => self
|
||||
.handle_seat_jump_to_mark(seat, kc)
|
||||
.wrn("seat_jump_to_mark")?,
|
||||
ClientMessage::SeatCopyMark { seat, src, dst } => self
|
||||
.handle_seat_copy_mark(seat, src, dst)
|
||||
.wrn("seat_copy_mark")?,
|
||||
ClientMessage::ConnectorSetBlendSpace {
|
||||
connector,
|
||||
blend_space,
|
||||
} => self
|
||||
.handle_connector_set_blend_space(connector, blend_space)
|
||||
.wrn("connector_set_blend_space")?,
|
||||
ClientMessage::SetBarFont { font } => self.handle_set_bar_font(font),
|
||||
ClientMessage::SetTitleFont { font } => self.handle_set_title_font(font),
|
||||
ClientMessage::ShowWorkspaceOn {
|
||||
seat,
|
||||
workspace,
|
||||
connector,
|
||||
} => self
|
||||
.handle_show_workspace(seat, workspace, Some(connector))
|
||||
.wrn("show_workspace_on")?,
|
||||
ClientMessage::SeatSetSimpleImEnabled { seat, enabled } => self
|
||||
.handle_seat_set_simple_im_enabled(seat, enabled)
|
||||
.wrn("seat_set_simple_im_enabled")?,
|
||||
ClientMessage::SeatGetSimpleImEnabled { seat } => self
|
||||
.handle_seat_get_simple_im_enabled(seat)
|
||||
.wrn("seat_get_simple_im_enabled")?,
|
||||
ClientMessage::SeatReloadSimpleIm { seat } => self
|
||||
.handle_seat_reload_simple_im(seat)
|
||||
.wrn("seat_reload_simple_im")?,
|
||||
ClientMessage::SeatEnableUnicodeInput { seat } => self
|
||||
.handle_seat_enable_unicode_input(seat)
|
||||
.wrn("seat_enable_unicode_input")?,
|
||||
ClientMessage::SeatWarpMouseToFocus { seat } => self
|
||||
.handle_seat_warp_mouse_to_focus(seat)
|
||||
.wrn("seat_warp_mouse_to_focus")?,
|
||||
ClientMessage::SeatSetMouseFollowsFocus { seat, enabled } => self
|
||||
.handle_seat_set_mouse_follows_focus(seat, enabled)
|
||||
.wrn("seat_set_mouse_follows_focus")?,
|
||||
ClientMessage::ConnectorSetUseNativeGamut {
|
||||
connector,
|
||||
use_native_gamut,
|
||||
} => self
|
||||
.handle_connector_set_use_native_gamut(connector, use_native_gamut)
|
||||
.wrn("connector_set_use_native_gamut")?,
|
||||
ClientMessage::KeymapFromNames {
|
||||
rules,
|
||||
model,
|
||||
groups,
|
||||
options,
|
||||
} => self
|
||||
.handle_keymap_from_names(rules, model, groups, options)
|
||||
.wrn("keymap_from_names")?,
|
||||
ClientMessage::SetFallbackOutputMode { seat, mode } => self
|
||||
.handle_set_fallback_output_mode(seat, mode)
|
||||
.wrn("set_fallback_output_mode")?,
|
||||
ClientMessage::SetXWaylandEnabled { enabled } => self
|
||||
.handle_set_x_wayland_enabled(enabled)
|
||||
.wrn("set_x_wayland_enabled")?,
|
||||
ClientMessage::ConnectorSupportsArbitraryModes { connector } => self
|
||||
.handle_connector_supports_arbitrary_modes(connector)
|
||||
.wrn("connector_supports_arbitrary_modes")?,
|
||||
ClientMessage::GetConnectorByName { name } => self.handle_get_connector_by_name(name),
|
||||
ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
|
||||
ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
|
||||
ClientMessage::CleanLogsOlderThan { time } => self.handle_clean_logs_older_than(time),
|
||||
ClientMessage::WindowResize {
|
||||
window,
|
||||
dx1,
|
||||
dy1,
|
||||
dx2,
|
||||
dy2,
|
||||
} => self
|
||||
.handle_window_resize(window, dx1, dy1, dx2, dy2)
|
||||
.wrn("window_resize")?,
|
||||
ClientMessage::SeatToggleTab { seat } => {
|
||||
self.handle_seat_toggle_tab(seat).wrn("seat_toggle_tab")?
|
||||
}
|
||||
ClientMessage::SeatMakeGroup {
|
||||
seat,
|
||||
axis,
|
||||
ephemeral,
|
||||
} => self
|
||||
.handle_seat_make_group(seat, axis, ephemeral)
|
||||
.wrn("seat_make_group")?,
|
||||
ClientMessage::SeatChangeGroupOpposite { seat } => self
|
||||
.handle_seat_change_group_opposite(seat)
|
||||
.wrn("seat_change_group_opposite")?,
|
||||
ClientMessage::SeatEqualize { seat, recursive } => self
|
||||
.handle_seat_equalize(seat, recursive)
|
||||
.wrn("seat_equalize")?,
|
||||
ClientMessage::SetAutotile { enabled } => {
|
||||
self.state.theme.autotile_enabled.set(enabled);
|
||||
}
|
||||
ClientMessage::GetAutotile => {
|
||||
self.respond(Response::GetAutotile {
|
||||
enabled: self.state.theme.autotile_enabled.get(),
|
||||
});
|
||||
}
|
||||
ClientMessage::SeatToggleExpand { .. } => {
|
||||
// Removed feature; kept for binary protocol compatibility.
|
||||
}
|
||||
ClientMessage::SetTabTitleAlign { align } => {
|
||||
use crate::theme::TabTitleAlign;
|
||||
let val = match align {
|
||||
1 => TabTitleAlign::Center,
|
||||
2 => TabTitleAlign::End,
|
||||
_ => TabTitleAlign::Start,
|
||||
};
|
||||
self.state.theme.tab_title_align.set(val);
|
||||
}
|
||||
ClientMessage::SeatMoveTab { seat, right } => self
|
||||
.handle_seat_move_tab(seat, right)
|
||||
.wrn("seat_move_tab")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
276
src/config/handler/input_devices.rs
Normal file
276
src/config/handler/input_devices.rs
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn handle_set_device_keymap(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
keymap: Keymap,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let map = if keymap.is_invalid() {
|
||||
None
|
||||
} else {
|
||||
Some(self.get_keymap(keymap)?)
|
||||
};
|
||||
dev.set_keymap(&self.state, map);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_input_device_connector(
|
||||
&self,
|
||||
input_device: InputDevice,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(input_device)?;
|
||||
let output = self.get_output_node(connector)?;
|
||||
dev.set_output(&self.state, Some(&output.global));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_remove_input_mapping(
|
||||
&self,
|
||||
input_device: InputDevice,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(input_device)?;
|
||||
dev.set_output(&self.state, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
seat: Seat,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = if seat.is_invalid() {
|
||||
None
|
||||
} else {
|
||||
Some(self.get_seat(seat)?)
|
||||
};
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_seat(&self.state, seat);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_left_handed(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
left_handed: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_left_handed(&self.state, left_handed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_accel_profile(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
accel_profile: AccelProfile,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let profile = match accel_profile {
|
||||
ACCEL_PROFILE_FLAT => InputDeviceAccelProfile::Flat,
|
||||
ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
|
||||
_ => return Err(CphError::UnknownAccelProfile(accel_profile)),
|
||||
};
|
||||
dev.set_accel_profile(&self.state, profile);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_accel_speed(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
speed: f64,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_accel_speed(&self.state, speed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_px_per_wheel_scroll(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
px: f64,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_px_per_scroll_wheel(&self.state, px);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_tap_enabled(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_tap_enabled(&self.state, enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_drag_enabled(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_drag_enabled(&self.state, enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_natural_scrolling_enabled(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_natural_scrolling_enabled(&self.state, enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_drag_lock_enabled(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_drag_lock_enabled(&self.state, enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_transform_matrix(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
matrix: [[f64; 2]; 2],
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_transform_matrix(&self.state, matrix);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_calibration_matrix(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
matrix: [[f32; 3]; 2],
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_calibration_matrix(&self.state, matrix);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_click_method(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
click_method: ClickMethod,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let method = match click_method {
|
||||
CLICK_METHOD_NONE => InputDeviceClickMethod::None,
|
||||
CLICK_METHOD_BUTTON_AREAS => InputDeviceClickMethod::ButtonAreas,
|
||||
CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
|
||||
_ => return Err(CphError::UnknownClickMethod(click_method)),
|
||||
};
|
||||
dev.set_click_method(&self.state, method);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_middle_button_emulation_enabled(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
dev.set_middle_button_emulation_enabled(&self.state, enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_device_name(&self, device: InputDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let name = dev.device.name();
|
||||
self.respond(Response::GetDeviceName {
|
||||
name: name.to_string(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_input_device_syspath(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
self.respond(Response::GetInputDeviceSyspath {
|
||||
syspath: dev.syspath.clone().unwrap_or_default(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_input_device_devnode(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
self.respond(Response::GetInputDeviceDevnode {
|
||||
devnode: dev.devnode.clone().unwrap_or_default(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_has_capability(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
cap: Capability,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let mut is_unknown = false;
|
||||
let has_cap = 'has_cap: {
|
||||
let cap = match cap {
|
||||
CAP_KEYBOARD => InputDeviceCapability::Keyboard,
|
||||
CAP_POINTER => InputDeviceCapability::Pointer,
|
||||
CAP_TOUCH => InputDeviceCapability::Touch,
|
||||
CAP_TABLET_TOOL => InputDeviceCapability::TabletTool,
|
||||
CAP_TABLET_PAD => InputDeviceCapability::TabletPad,
|
||||
CAP_GESTURE => InputDeviceCapability::Gesture,
|
||||
CAP_SWITCH => InputDeviceCapability::Switch,
|
||||
_ => {
|
||||
is_unknown = true;
|
||||
break 'has_cap false;
|
||||
}
|
||||
};
|
||||
dev.device.has_capability(cap)
|
||||
};
|
||||
self.respond(Response::HasCapability { has: has_cap });
|
||||
if is_unknown {
|
||||
Err(CphError::UnknownCapability(cap))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_input_devices(&self, seat: Option<Seat>) {
|
||||
let id = seat.map(|s| SeatId::from_raw(s.0 as _));
|
||||
let matches = |dhd: &DeviceHandlerData| {
|
||||
let id = match id {
|
||||
Some(id) => id,
|
||||
_ => return true,
|
||||
};
|
||||
if let Some(seat) = dhd.seat.get() {
|
||||
return seat.id() == id;
|
||||
}
|
||||
false
|
||||
};
|
||||
let mut res = vec![];
|
||||
{
|
||||
let devs = self.state.input_device_handlers.borrow_mut();
|
||||
for dev in devs.values() {
|
||||
if matches(&dev.data) {
|
||||
res.push(InputDevice(dev.id.raw() as _));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.respond(Response::GetInputDevices { devices: res });
|
||||
}
|
||||
|
||||
pub(super) fn handle_grab(&self, kb: InputDevice, grab: bool) -> Result<(), CphError> {
|
||||
let kb = self.get_kb(kb)?;
|
||||
kb.grab(grab);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
64
src/config/handler/keymaps.rs
Normal file
64
src/config/handler/keymaps.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
fn id(&self) -> u64 {
|
||||
self.next_id.fetch_add(1)
|
||||
}
|
||||
|
||||
pub(super) fn handle_parse_keymap(&self, keymap: &str) -> Result<(), CphError> {
|
||||
let (keymap, res) = match self.state.kb_ctx.parse_keymap(keymap.as_bytes()) {
|
||||
Ok(keymap) => {
|
||||
let id = Keymap(self.id());
|
||||
self.keymaps.set(id, keymap);
|
||||
(id, Ok(()))
|
||||
}
|
||||
Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
|
||||
};
|
||||
self.respond(Response::ParseKeymap { keymap });
|
||||
res
|
||||
}
|
||||
|
||||
pub(super) fn handle_keymap_from_names(
|
||||
&self,
|
||||
rules: Option<&str>,
|
||||
model: Option<&str>,
|
||||
groups: Option<Vec<Group<'_>>>,
|
||||
options: Option<Vec<&str>>,
|
||||
) -> Result<(), CphError> {
|
||||
let kbvm_groups = groups.map(|groups| {
|
||||
groups
|
||||
.iter()
|
||||
.map(|g| kbvm::xkb::rmlvo::Group {
|
||||
layout: g.layout,
|
||||
variant: g.variant,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
let (keymap, res) = match self.state.kb_ctx.keymap_from_names(
|
||||
rules,
|
||||
model,
|
||||
kbvm_groups.as_deref(),
|
||||
options.as_deref(),
|
||||
) {
|
||||
Ok(keymap) => {
|
||||
let id = Keymap(self.id());
|
||||
self.keymaps.set(id, keymap);
|
||||
(id, Ok(()))
|
||||
}
|
||||
Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
|
||||
};
|
||||
self.respond(Response::KeymapFromNames { keymap });
|
||||
res
|
||||
}
|
||||
|
||||
pub(super) fn get_keymap(&self, keymap: Keymap) -> Result<Rc<KbvmMap>, CphError> {
|
||||
match self.keymaps.get(&keymap) {
|
||||
Some(k) => Ok(k),
|
||||
None => Err(CphError::KeymapDoesNotExist(keymap)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_destroy_keymap(&self, keymap: Keymap) {
|
||||
self.keymaps.remove(&keymap);
|
||||
}
|
||||
}
|
||||
300
src/config/handler/matchers.rs
Normal file
300
src/config/handler/matchers.rs
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
fn get_client_matcher(
|
||||
&self,
|
||||
matcher: ClientMatcher,
|
||||
) -> Result<Rc<CachedCriterion<ClientCriterionPayload, Client>>, CphError> {
|
||||
self.client_matchers
|
||||
.get(&matcher)
|
||||
.ok_or(CphError::ClientMatcherDoesNotExist(matcher))
|
||||
}
|
||||
|
||||
fn sort_generic_matcher<T, K>(
|
||||
&self,
|
||||
generic: &mut GenericCriterionPayload<T>,
|
||||
key: impl FnMut(&T) -> K,
|
||||
) where
|
||||
K: Ord,
|
||||
{
|
||||
match generic {
|
||||
GenericCriterionPayload::List { list, .. } | GenericCriterionPayload::Exactly { list, .. } => {
|
||||
list.sort_by_key(key)
|
||||
}
|
||||
GenericCriterionPayload::Matcher(_) | GenericCriterionPayload::Not(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_generic_matcher<Crit, Matcher, Mgr>(
|
||||
&self,
|
||||
mgr: &Mgr,
|
||||
generic: &GenericCriterionPayload<Matcher>,
|
||||
upstream: &mut Vec<Rc<CachedCriterion<Crit, Mgr::Target>>>,
|
||||
get_matcher: impl Fn(&Matcher) -> Result<Rc<CachedCriterion<Crit, Mgr::Target>>, CphError>,
|
||||
) -> Result<Rc<dyn CritUpstreamNode<Mgr::Target>>, CphError>
|
||||
where
|
||||
Crit: Clone + Hash + Eq,
|
||||
Mgr: CritMgrExt,
|
||||
{
|
||||
let mut get_upstream = |m: &Matcher| -> Result<_, CphError> {
|
||||
let m = get_matcher(m)?;
|
||||
let node = m.node.clone();
|
||||
upstream.push(m);
|
||||
Ok(node)
|
||||
};
|
||||
let node = match generic {
|
||||
GenericCriterionPayload::Matcher(m) => get_matcher(m)?.node.clone(),
|
||||
GenericCriterionPayload::Not(m) => mgr.not(&get_upstream(m)?),
|
||||
GenericCriterionPayload::List { list, all } => {
|
||||
let mut m = Vec::with_capacity(list.len());
|
||||
for c in list {
|
||||
m.push(get_upstream(c)?);
|
||||
}
|
||||
mgr.list(&m, *all)
|
||||
}
|
||||
GenericCriterionPayload::Exactly { list, num } => {
|
||||
let mut m = Vec::with_capacity(list.len());
|
||||
for c in list {
|
||||
m.push(get_upstream(c)?);
|
||||
}
|
||||
mgr.exactly(&m, *num)
|
||||
}
|
||||
};
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
pub(super) fn handle_create_client_matcher(
|
||||
&self,
|
||||
mut criterion: ClientCriterionPayload,
|
||||
) -> Result<(), CphError> {
|
||||
if let ClientCriterionPayload::Generic(generic) = &mut criterion {
|
||||
self.sort_generic_matcher(generic, |m| m.0);
|
||||
}
|
||||
let id = ClientMatcher(self.client_matcher_ids.fetch_add(1));
|
||||
let cache = &self.client_matcher_cache;
|
||||
if let Some(matcher) = cache.get(&criterion)
|
||||
&& let Some(matcher) = matcher.upgrade()
|
||||
{
|
||||
self.client_matchers.set(id, matcher);
|
||||
self.respond(Response::CreateClientMatcher { matcher: id });
|
||||
return Ok(());
|
||||
}
|
||||
let mgr = &self.state.cl_matcher_manager;
|
||||
let mut upstream = vec![];
|
||||
let matcher = match &criterion {
|
||||
ClientCriterionPayload::Generic(m) => {
|
||||
self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_client_matcher(*m))?
|
||||
}
|
||||
ClientCriterionPayload::String {
|
||||
string,
|
||||
field,
|
||||
regex,
|
||||
} => {
|
||||
let needle = match *regex {
|
||||
true => {
|
||||
let regex = Regex::new(string).map_err(CphError::InvalidRegex)?;
|
||||
CritLiteralOrRegex::Regex(regex)
|
||||
}
|
||||
false => CritLiteralOrRegex::Literal(string.to_string()),
|
||||
};
|
||||
match *field {
|
||||
ClientCriterionStringField::SandboxEngine => mgr.sandbox_engine(needle),
|
||||
ClientCriterionStringField::SandboxAppId => mgr.sandbox_app_id(needle),
|
||||
ClientCriterionStringField::SandboxInstanceId => {
|
||||
mgr.sandbox_instance_id(needle)
|
||||
}
|
||||
ClientCriterionStringField::Comm => mgr.comm(needle),
|
||||
ClientCriterionStringField::Exe => mgr.exe(needle),
|
||||
ClientCriterionStringField::Tag => mgr.tag(needle),
|
||||
}
|
||||
}
|
||||
ClientCriterionPayload::Sandboxed => mgr.sandboxed(),
|
||||
ClientCriterionPayload::Uid(p) => mgr.uid(*p),
|
||||
ClientCriterionPayload::Pid(p) => mgr.pid(*p),
|
||||
ClientCriterionPayload::IsXwayland => mgr.is_xwayland(),
|
||||
};
|
||||
let cached = Rc::new(CachedCriterion {
|
||||
crit: criterion.clone(),
|
||||
cache: cache.clone(),
|
||||
upstream,
|
||||
node: matcher.clone(),
|
||||
});
|
||||
cache.set(criterion, Rc::downgrade(&cached));
|
||||
self.client_matchers.set(id, cached);
|
||||
self.respond(Response::CreateClientMatcher { matcher: id });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_destroy_client_matcher(&self, matcher: ClientMatcher) {
|
||||
self.client_matchers.remove(&matcher);
|
||||
self.client_matcher_leafs.remove(&matcher);
|
||||
}
|
||||
|
||||
pub(super) fn handle_enable_client_matcher_events(
|
||||
self: &Rc<Self>,
|
||||
matcher: ClientMatcher,
|
||||
) -> Result<(), CphError> {
|
||||
if self.client_matcher_leafs.contains(&matcher) {
|
||||
return Ok(());
|
||||
}
|
||||
let upstream = self.get_client_matcher(matcher)?;
|
||||
let slf = self.clone();
|
||||
let leaf = self
|
||||
.state
|
||||
.cl_matcher_manager
|
||||
.leaf(&upstream.node, move |id| {
|
||||
let client = ConfigClient(id.raw());
|
||||
slf.send(&ServerMessage::ClientMatcherMatched { matcher, client });
|
||||
let slf = slf.clone();
|
||||
Box::new(move || {
|
||||
slf.send(&ServerMessage::ClientMatcherUnmatched { matcher, client });
|
||||
})
|
||||
});
|
||||
self.client_matcher_leafs.set(matcher, leaf);
|
||||
self.state.cl_matcher_manager.rematch_all(&self.state);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_window_matcher(
|
||||
&self,
|
||||
matcher: WindowMatcher,
|
||||
) -> Result<Rc<CachedCriterion<WindowCriterionPayload, ToplevelData>>, CphError> {
|
||||
self.window_matchers
|
||||
.get(&matcher)
|
||||
.ok_or(CphError::WindowMatcherDoesNotExist(matcher))
|
||||
}
|
||||
|
||||
pub(super) fn handle_create_window_matcher(
|
||||
&self,
|
||||
mut criterion: WindowCriterionPayload,
|
||||
) -> Result<(), CphError> {
|
||||
if let WindowCriterionPayload::Generic(generic) = &mut criterion {
|
||||
self.sort_generic_matcher(generic, |m| m.0);
|
||||
}
|
||||
let id = WindowMatcher(self.window_matcher_ids.fetch_add(1));
|
||||
let cache = &self.window_matcher_cache;
|
||||
if let Some(matcher) = cache.get(&criterion)
|
||||
&& let Some(matcher) = matcher.upgrade()
|
||||
{
|
||||
self.window_matchers.set(id, matcher);
|
||||
self.respond(Response::CreateWindowMatcher { matcher: id });
|
||||
return Ok(());
|
||||
}
|
||||
let mgr = &self.state.tl_matcher_manager;
|
||||
let mut upstream = vec![];
|
||||
let matcher = match &criterion {
|
||||
WindowCriterionPayload::Generic(m) => {
|
||||
self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_window_matcher(*m))?
|
||||
}
|
||||
WindowCriterionPayload::String {
|
||||
string,
|
||||
field,
|
||||
regex,
|
||||
} => {
|
||||
let needle = match *regex {
|
||||
true => {
|
||||
let regex = Regex::new(string).map_err(CphError::InvalidRegex)?;
|
||||
CritLiteralOrRegex::Regex(regex)
|
||||
}
|
||||
false => CritLiteralOrRegex::Literal(string.to_string()),
|
||||
};
|
||||
match *field {
|
||||
WindowCriterionStringField::Title => mgr.title(needle),
|
||||
WindowCriterionStringField::AppId => mgr.app_id(needle),
|
||||
WindowCriterionStringField::Tag => mgr.tag(needle),
|
||||
WindowCriterionStringField::XClass => mgr.class(needle),
|
||||
WindowCriterionStringField::XInstance => mgr.instance(needle),
|
||||
WindowCriterionStringField::XRole => mgr.role(needle),
|
||||
WindowCriterionStringField::Workspace => mgr.workspace(needle),
|
||||
}
|
||||
}
|
||||
WindowCriterionPayload::Types(t) => mgr.kind(*t),
|
||||
WindowCriterionPayload::Client(c) => {
|
||||
self.state.cl_matcher_manager.rematch_all(&self.state);
|
||||
mgr.client(&self.state, &self.get_client_matcher(*c)?.node)
|
||||
}
|
||||
WindowCriterionPayload::Floating => mgr.floating(),
|
||||
WindowCriterionPayload::Visible => mgr.visible(),
|
||||
WindowCriterionPayload::Urgent => mgr.urgent(),
|
||||
WindowCriterionPayload::SeatFocus(seat) => mgr.seat_focus(&*self.get_seat(*seat)?),
|
||||
WindowCriterionPayload::Fullscreen => mgr.fullscreen(),
|
||||
WindowCriterionPayload::JustMapped => mgr.just_mapped(),
|
||||
WindowCriterionPayload::Workspace(w) => mgr.workspace(CritLiteralOrRegex::Literal(
|
||||
self.get_workspace(*w)?.to_string(),
|
||||
)),
|
||||
WindowCriterionPayload::ContentTypes(t) => mgr.content_type(*t),
|
||||
};
|
||||
let cached = Rc::new(CachedCriterion {
|
||||
crit: criterion.clone(),
|
||||
cache: cache.clone(),
|
||||
upstream,
|
||||
node: matcher.clone(),
|
||||
});
|
||||
cache.set(criterion, Rc::downgrade(&cached));
|
||||
self.window_matchers.set(id, cached);
|
||||
self.respond(Response::CreateWindowMatcher { matcher: id });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_destroy_window_matcher(&self, matcher: WindowMatcher) {
|
||||
self.window_matchers.remove(&matcher);
|
||||
self.window_matcher_leafs.remove(&matcher);
|
||||
self.window_matcher_no_auto_focus.remove(&matcher);
|
||||
self.window_matcher_initial_tile_state.remove(&matcher);
|
||||
}
|
||||
|
||||
pub(super) fn handle_enable_window_matcher_events(
|
||||
self: &Rc<Self>,
|
||||
matcher: WindowMatcher,
|
||||
) -> Result<(), CphError> {
|
||||
if self.window_matcher_leafs.contains(&matcher) {
|
||||
return Ok(());
|
||||
}
|
||||
let upstream = self.get_window_matcher(matcher)?;
|
||||
let mut node = upstream.node.clone();
|
||||
if !upstream.any(&|crit| matches!(crit, WindowCriterionPayload::Types(_))) {
|
||||
let list = [self.window_matcher_std_kinds.clone(), node];
|
||||
node = self.state.tl_matcher_manager.list(&list, true);
|
||||
}
|
||||
let slf = self.clone();
|
||||
let leaf = self.state.tl_matcher_manager.leaf(&node, move |tl| {
|
||||
let window = slf.tl_id_to_window(tl);
|
||||
slf.send(&ServerMessage::WindowMatcherMatched { matcher, window });
|
||||
let slf = slf.clone();
|
||||
Box::new(move || {
|
||||
slf.send(&ServerMessage::WindowMatcherUnmatched { matcher, window });
|
||||
})
|
||||
});
|
||||
self.window_matcher_leafs.set(matcher, leaf);
|
||||
self.state.tl_matcher_manager.rematch_all(&self.state);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_matcher_auto_focus(
|
||||
&self,
|
||||
matcher: WindowMatcher,
|
||||
auto_focus: bool,
|
||||
) -> Result<(), CphError> {
|
||||
if auto_focus {
|
||||
self.window_matcher_no_auto_focus.remove(&matcher);
|
||||
} else {
|
||||
let m = self.get_window_matcher(matcher)?;
|
||||
self.window_matcher_no_auto_focus.set(matcher, m);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_matcher_initial_tile_state(
|
||||
&self,
|
||||
matcher: WindowMatcher,
|
||||
tile_state: ConfigTileState,
|
||||
) -> Result<(), CphError> {
|
||||
let Ok(tile_state) = tile_state.try_into() else {
|
||||
return Err(CphError::UnknownTileState(tile_state));
|
||||
};
|
||||
let m = self.get_window_matcher(matcher)?;
|
||||
self.window_matcher_initial_tile_state
|
||||
.set(matcher, (m, tile_state));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
131
src/config/handler/options.rs
Normal file
131
src/config/handler/options.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn handle_set_ei_socket_enabled(&self, enabled: bool) {
|
||||
self.state.set_ei_socket_enabled(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_gfx_api(
|
||||
&self,
|
||||
device: Option<DrmDevice>,
|
||||
api: GfxApi,
|
||||
) -> Result<(), CphError> {
|
||||
let Ok(api) = api.try_into() else {
|
||||
return Err(CphError::UnknownGfxApi(api));
|
||||
};
|
||||
match device {
|
||||
Some(dev) => self.get_drm_device(dev)?.dev.set_gfx_api(api),
|
||||
_ => self.state.default_gfx_api.set(api),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_flip_margin(
|
||||
&self,
|
||||
device: DrmDevice,
|
||||
margin: Duration,
|
||||
) -> Result<(), CphError> {
|
||||
self.get_drm_device(device)?.set_flip_margin(
|
||||
&self.state,
|
||||
margin.as_nanos().try_into().unwrap_or(u64::MAX),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_x_scaling_mode(
|
||||
&self,
|
||||
mode: XScalingMode,
|
||||
) -> Result<(), CphError> {
|
||||
let use_wire_scale = match mode {
|
||||
XScalingMode::DEFAULT => false,
|
||||
XScalingMode::DOWNSCALED => true,
|
||||
_ => return Err(CphError::UnknownXScalingMode(mode)),
|
||||
};
|
||||
self.state.set_xwayland_use_wire_scale(use_wire_scale);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_x_wayland_enabled(&self, enabled: bool) -> Result<(), CphError> {
|
||||
self.state.set_xwayland_enabled(enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_ui_drag_enabled(&self, enabled: bool) {
|
||||
self.state.set_ui_drag_enabled(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_ui_drag_threshold(&self, threshold: i32) {
|
||||
self.state.set_ui_drag_threshold(threshold.max(1));
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_animations_enabled(&self, enabled: bool) {
|
||||
self.state.set_animations_enabled(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_animation_duration_ms(&self, duration_ms: u32) {
|
||||
self.state
|
||||
.set_animation_duration_ms(duration_ms.min(10_000));
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_animation_curve(&self, curve: u32) {
|
||||
self.state.set_animation_curve(curve);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_animation_style(&self, style: u32) {
|
||||
if !self.state.set_animation_style(style) {
|
||||
log::warn!("Ignoring invalid animation style");
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_animation_cubic_bezier(&self, x1: f32, y1: f32, x2: f32, y2: f32) {
|
||||
if !self.state.set_animation_cubic_bezier(x1, y1, x2, y2) {
|
||||
log::warn!("Ignoring invalid animation cubic-bezier curve");
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_direct_scanout_enabled(
|
||||
&self,
|
||||
device: Option<DrmDevice>,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
match device {
|
||||
Some(dev) => self
|
||||
.get_drm_device(dev)?
|
||||
.set_direct_scanout_enabled(&self.state, enabled),
|
||||
_ => self.state.direct_scanout_enabled.set(enabled),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_double_click_interval_usec(&self, usec: u64) {
|
||||
self.state.double_click_interval_usec.set(usec);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_double_click_distance(&self, dist: i32) {
|
||||
self.state.double_click_distance.set(dist);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_idle(&self, timeout: Duration) {
|
||||
self.state.idle.set_timeout(&self.state, timeout);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_key_press_enables_dpms(&self, enabled: bool) {
|
||||
self.state.idle.key_press_enables_dpms.set(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_mouse_move_enables_dpms(&self, enabled: bool) {
|
||||
self.state.idle.mouse_move_enables_dpms.set(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_idle_grace_period(&self, period: Duration) {
|
||||
self.state.idle.set_grace_period(&self.state, period);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
|
||||
self.state.set_explicit_sync_enabled(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_color_management_enabled(&self, enabled: bool) {
|
||||
self.state.set_color_management_enabled(enabled);
|
||||
}
|
||||
}
|
||||
499
src/config/handler/outputs.rs
Normal file
499
src/config/handler/outputs.rs
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn handle_get_connectors(
|
||||
&self,
|
||||
dev: Option<DrmDevice>,
|
||||
connected_only: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let datas: Vec<_>;
|
||||
if let Some(dev) = dev {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
datas = dev.connectors.lock().values().cloned().collect();
|
||||
} else {
|
||||
datas = self.state.connectors.lock().values().cloned().collect();
|
||||
}
|
||||
let connectors = datas
|
||||
.iter()
|
||||
.flat_map(|d| match (connected_only, d.connected.get()) {
|
||||
(false, _) | (true, true) => Some(Connector(d.connector.id().raw() as _)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
self.respond(Response::GetConnectors { connectors });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_drm_device_syspath(&self, dev: DrmDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
let syspath = dev.syspath.clone().unwrap_or_default();
|
||||
self.respond(Response::GetDrmDeviceSyspath { syspath });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_drm_device_devnode(&self, dev: DrmDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
let devnode = dev.devnode.clone().unwrap_or_default();
|
||||
self.respond(Response::GetDrmDeviceDevnode { devnode });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_drm_device_vendor(&self, dev: DrmDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
let vendor = dev.vendor.clone().unwrap_or_default();
|
||||
self.respond(Response::GetDrmDeviceVendor { vendor });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_drm_devices(&self) {
|
||||
let devs = self.state.drm_devs.lock();
|
||||
let mut res = vec![];
|
||||
for dev in devs.values() {
|
||||
res.push(DrmDevice(dev.dev.id().raw() as _));
|
||||
}
|
||||
self.respond(Response::GetDrmDevices { devices: res });
|
||||
}
|
||||
|
||||
pub(super) fn handle_make_render_device(&self, dev: DrmDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
dev.make_render_device();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_drm_device_model(&self, dev: DrmDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
let model = dev.model.clone().unwrap_or_default();
|
||||
self.respond(Response::GetDrmDeviceModel { model });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_drm_device_pci_id(&self, dev: DrmDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_drm_device(dev)?;
|
||||
let pci_id = dev.pci_id.unwrap_or_default();
|
||||
self.respond(Response::GetDrmDevicePciId { pci_id });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_connected(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
self.respond(Response::ConnectorConnected {
|
||||
connected: connector.connected.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_type(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
self.respond(Response::ConnectorType {
|
||||
ty: connector.connector.kernel_id().ty.to_config(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_mode(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
let mode = connector.global.mode.get();
|
||||
self.respond(Response::ConnectorMode {
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
refresh_millihz: mode.refresh_rate_millihz,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_mode(
|
||||
&self,
|
||||
connector: Connector,
|
||||
mode: WireMode,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
connector
|
||||
.modify_state(&self.state, |s| {
|
||||
s.mode = backend::Mode {
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
refresh_rate_millihz: mode.refresh_millihz,
|
||||
};
|
||||
})
|
||||
.map_err(CphError::ModifyConnectorState)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_modes(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
self.respond(Response::ConnectorModes {
|
||||
modes: connector
|
||||
.global
|
||||
.modes
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|m| WireMode {
|
||||
width: m.width,
|
||||
height: m.height,
|
||||
refresh_millihz: m.refresh_rate_millihz,
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_supports_arbitrary_modes(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
self.respond(Response::ConnectorSupportsArbitraryModes {
|
||||
supports_arbitrary_modes: connector.global.modes.is_none(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_name(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
self.respond(Response::GetConnectorName {
|
||||
name: connector.name.deref().clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_model(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
self.respond(Response::GetConnectorModel {
|
||||
model: connector.monitor_info.output_id.model.clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_manufacturer(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
self.respond(Response::GetConnectorManufacturer {
|
||||
manufacturer: connector.monitor_info.output_id.manufacturer.clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_serial_number(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
self.respond(Response::GetConnectorSerialNumber {
|
||||
serial_number: connector.monitor_info.output_id.serial_number.clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_size(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
let pos = connector.global.pos.get();
|
||||
self.respond(Response::ConnectorSize {
|
||||
width: pos.width(),
|
||||
height: pos.height(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
self.respond(Response::ConnectorGetScale {
|
||||
scale: connector.global.persistent.scale.get().to_f64(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_scale(
|
||||
&self,
|
||||
connector: Connector,
|
||||
scale: f64,
|
||||
) -> Result<(), CphError> {
|
||||
if scale < 0.1 {
|
||||
return Err(CphError::ScaleTooSmall(scale));
|
||||
}
|
||||
if scale > 1000.0 {
|
||||
return Err(CphError::ScaleTooLarge(scale));
|
||||
}
|
||||
let scale = Scale::from_f64(scale);
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_preferred_scale(scale);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_format(
|
||||
&self,
|
||||
connector: Connector,
|
||||
format: ConfigFormat,
|
||||
) -> Result<(), CphError> {
|
||||
let Some(&format) = config_formats().get(&format) else {
|
||||
return Err(CphError::UnknownFormat(format));
|
||||
};
|
||||
let connector = self.get_connector(connector)?;
|
||||
connector
|
||||
.modify_state(&self.state, |s| s.format = format)
|
||||
.map_err(CphError::ModifyConnectorState)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_colors(
|
||||
&self,
|
||||
connector: Connector,
|
||||
color_space: ColorSpace,
|
||||
eotf: ConfigEotf,
|
||||
) -> Result<(), CphError> {
|
||||
let bcs = match color_space {
|
||||
ColorSpace::DEFAULT => BackendColorSpace::Default,
|
||||
ColorSpace::BT2020 => BackendColorSpace::Bt2020,
|
||||
_ => return Err(CphError::UnknownColorSpace(color_space)),
|
||||
};
|
||||
let btf = match eotf {
|
||||
ConfigEotf::DEFAULT => BackendEotfs::Default,
|
||||
ConfigEotf::PQ => BackendEotfs::Pq,
|
||||
_ => return Err(CphError::UnknownEotf(eotf)),
|
||||
};
|
||||
let connector = self.get_connector(connector)?;
|
||||
connector
|
||||
.modify_state(&self.state, |s| {
|
||||
s.color_space = bcs;
|
||||
s.eotf = btf;
|
||||
})
|
||||
.map_err(CphError::ModifyConnectorState)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_blend_space(
|
||||
&self,
|
||||
connector: Connector,
|
||||
blend_space: ConfigBlendSpace,
|
||||
) -> Result<(), CphError> {
|
||||
let blend_space = match blend_space {
|
||||
ConfigBlendSpace::SRGB => BlendSpace::Srgb,
|
||||
ConfigBlendSpace::LINEAR => BlendSpace::Linear,
|
||||
_ => return Err(CphError::UnknownBlendSpace(blend_space)),
|
||||
};
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_blend_space(blend_space);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_brightness(
|
||||
&self,
|
||||
connector: Connector,
|
||||
brightness: Option<f64>,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_brightness(brightness);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_use_native_gamut(
|
||||
&self,
|
||||
connector: Connector,
|
||||
use_native_gamut: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_use_native_gamut(use_native_gamut);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_vrr_mode(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
mode: ConfigVrrMode,
|
||||
) -> Result<(), CphError> {
|
||||
let Some(mode) = VrrMode::from_config(mode) else {
|
||||
return Err(CphError::UnknownVrrMode(mode));
|
||||
};
|
||||
match connector {
|
||||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.set_vrr_mode(mode);
|
||||
}
|
||||
_ => self.state.default_vrr_mode.set(*mode),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_vrr_cursor_hz(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
hz: f64,
|
||||
) -> Result<(), CphError> {
|
||||
match connector {
|
||||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.schedule.set_cursor_hz(hz);
|
||||
}
|
||||
_ => {
|
||||
let Some((hz, _)) = map_cursor_hz(hz) else {
|
||||
return Err(CphError::InvalidCursorHz(hz));
|
||||
};
|
||||
self.state.default_vrr_cursor_hz.set(hz)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_tearing_mode(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
mode: ConfigTearingMode,
|
||||
) -> Result<(), CphError> {
|
||||
let Some(mode) = TearingMode::from_config(mode) else {
|
||||
return Err(CphError::UnknownTearingMode(mode));
|
||||
};
|
||||
match connector {
|
||||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.set_tearing_mode(mode);
|
||||
}
|
||||
_ => self.state.default_tearing_mode.set(*mode),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_transform(
|
||||
&self,
|
||||
connector: Connector,
|
||||
transform: Transform,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.update_transform(transform.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_position(
|
||||
&self,
|
||||
connector: Connector,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
if x < 0 || y < 0 || x > MAX_EXTENTS || y > MAX_EXTENTS {
|
||||
return Err(CphError::InvalidConnectorPosition(x, y));
|
||||
}
|
||||
connector.set_position(x, y);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_get_position(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
let (x, y) = connector.global.pos.get().position();
|
||||
self.respond(Response::ConnectorGetPosition { x, y });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_connector_set_enabled(
|
||||
&self,
|
||||
connector: Connector,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
connector
|
||||
.modify_state(&self.state, |s| {
|
||||
s.enabled = enabled;
|
||||
})
|
||||
.map_err(CphError::ModifyConnectorState)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_connector(
|
||||
&self,
|
||||
ty: jay_config::video::connector_type::ConnectorType,
|
||||
idx: u32,
|
||||
) -> Result<(), CphError> {
|
||||
let connectors = self.state.connectors.lock();
|
||||
let connector = 'get_connector: {
|
||||
for connector in connectors.values() {
|
||||
let kid = connector.connector.kernel_id();
|
||||
if ty == kid.ty.to_config() && idx == kid.idx {
|
||||
break 'get_connector Connector(connector.connector.id().raw() as _);
|
||||
}
|
||||
}
|
||||
Connector(0)
|
||||
};
|
||||
self.respond(Response::GetConnector { connector });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_connector_by_name(&self, name: &str) {
|
||||
let connector = self
|
||||
.state
|
||||
.connectors
|
||||
.lock()
|
||||
.values()
|
||||
.find(|c| *c.name == name)
|
||||
.map(|c| c.connector.id().raw() as _)
|
||||
.map(Connector)
|
||||
.unwrap_or(Connector(0));
|
||||
self.respond(Response::GetConnector { connector });
|
||||
}
|
||||
|
||||
pub(super) fn handle_create_virtual_output(&self, name: &str) {
|
||||
self.state.virtual_outputs.get_or_create(&self.state, name);
|
||||
}
|
||||
|
||||
pub(super) fn handle_remove_virtual_output(&self, name: &str) {
|
||||
self.state.virtual_outputs.remove_output(&self.state, name);
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_connector_active_workspace(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let output = self.get_output_node(connector)?;
|
||||
let workspace = output
|
||||
.workspace
|
||||
.get()
|
||||
.map_or(Workspace(0), |ws| self.get_workspace_by_name(&ws.name));
|
||||
self.respond(Response::GetConnectorActiveWorkspace { workspace });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_connector_workspaces(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let output = self.get_output_node(connector)?;
|
||||
let workspaces = output
|
||||
.workspaces
|
||||
.iter()
|
||||
.map(|ws| self.get_workspace_by_name(&ws.name))
|
||||
.collect::<Vec<_>>();
|
||||
self.respond(Response::GetConnectorWorkspaces { workspaces });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_workspace_connector(
|
||||
&self,
|
||||
workspace: Workspace,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self
|
||||
.get_existing_workspace(workspace)?
|
||||
.map(|ws| ws.output.get())
|
||||
.filter(|o| !o.is_dummy)
|
||||
.map(|o| Connector(o.global.connector.id.raw() as _))
|
||||
.unwrap_or(Connector(0));
|
||||
self.respond(Response::GetWorkspaceConnector { connector });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_connector_in_direction(
|
||||
&self,
|
||||
connector: Connector,
|
||||
direction: Direction,
|
||||
) -> Result<(), CphError> {
|
||||
let source_output = self.get_output_node(connector)?;
|
||||
let connector = self
|
||||
.state
|
||||
.find_output_in_direction(&source_output, direction.into())
|
||||
.map(|o| Connector(o.global.connector.id.raw() as u64))
|
||||
.unwrap_or(Connector(0));
|
||||
self.respond(Response::GetConnectorInDirection { connector });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
137
src/config/handler/resources.rs
Normal file
137
src/config/handler/resources.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
fn get_timer(&self, timer: JayTimer) -> Result<Rc<TimerData>, CphError> {
|
||||
match self.timers_by_id.get(&timer.0) {
|
||||
Some(t) => Ok(t),
|
||||
_ => Err(CphError::TimerDoesNotExist(timer)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_remove_timer(&self, timer: JayTimer) -> Result<(), CphError> {
|
||||
let timer = self.get_timer(timer)?;
|
||||
self.timers_by_id.remove(&timer.id);
|
||||
self.timers_by_name.remove(&timer.name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_program_timer(
|
||||
&self,
|
||||
timer: JayTimer,
|
||||
initial: Option<Duration>,
|
||||
periodic: Option<Duration>,
|
||||
) -> Result<(), CphError> {
|
||||
let timer = self.get_timer(timer)?;
|
||||
timer.timer.program(initial, periodic)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_timer(self: &Rc<Self>, name: &str) -> Result<(), CphError> {
|
||||
let name = Rc::new(name.to_owned());
|
||||
if let Some(t) = self.timers_by_name.get(&name) {
|
||||
self.respond(Response::GetTimer {
|
||||
timer: JayTimer(t.id),
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
let id = self.timer_ids.fetch_add(1);
|
||||
let timer = TimerFd::new(c::CLOCK_BOOTTIME)?;
|
||||
let handler = {
|
||||
let timer = timer.clone();
|
||||
let slf = self.clone();
|
||||
self.state.eng.spawn("config timer", async move {
|
||||
loop {
|
||||
match timer.expired(&slf.state.ring).await {
|
||||
Ok(_) => slf.send(&ServerMessage::TimerExpired {
|
||||
timer: JayTimer(id),
|
||||
}),
|
||||
Err(e) => {
|
||||
log::error!("Could not wait for timer expiration: {}", ErrorFmt(e));
|
||||
if let Some(timer) = slf.timers_by_id.remove(&id) {
|
||||
slf.timers_by_name.remove(&timer.name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
let td = Rc::new(TimerData {
|
||||
timer,
|
||||
id,
|
||||
name: name.clone(),
|
||||
_handler: handler,
|
||||
});
|
||||
self.timers_by_name.set(name.clone(), td.clone());
|
||||
self.timers_by_id.set(id, td.clone());
|
||||
self.respond(Response::GetTimer {
|
||||
timer: JayTimer(id),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_add_pollable(self: &Rc<Self>, fd: i32) -> Result<(), CphError> {
|
||||
let fd = match fcntl_dupfd_cloexec(fd, 0).to_os_error() {
|
||||
Ok(fd) => Rc::new(fd),
|
||||
Err(e) => {
|
||||
let err = format!("Could not invoke F_DUPFD_CLOEXEC: {}", ErrorFmt(e));
|
||||
log::error!("{}", err);
|
||||
self.respond(Response::AddPollable { id: Err(err) });
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let id = self.pollable_id.fetch_add(1);
|
||||
let id = PollableId(id);
|
||||
let create = |writable: bool, events: c::c_short| {
|
||||
let event = Rc::new(AsyncEvent::default());
|
||||
let slf = self.clone();
|
||||
let trigger = event.clone();
|
||||
let fd = fd.clone();
|
||||
let future = self.state.eng.spawn("config fd poller", async move {
|
||||
loop {
|
||||
trigger.triggered().await;
|
||||
let res = slf.state.ring.poll(&fd, events).await.merge();
|
||||
if let Err(e) = &res {
|
||||
log::warn!("Could not poll fd: {}", ErrorFmt(e));
|
||||
}
|
||||
let res = res.map_err(|e| ErrorFmt(e).to_string()).map(drop);
|
||||
slf.send(&ServerMessage::InterestReady { id, writable, res });
|
||||
}
|
||||
});
|
||||
(event, future)
|
||||
};
|
||||
let (read_trigger, _read_future) = create(false, c::POLLIN);
|
||||
let (write_trigger, _write_future) = create(true, c::POLLOUT);
|
||||
self.pollables.set(
|
||||
id,
|
||||
Rc::new(Pollable {
|
||||
write_trigger,
|
||||
_write_future,
|
||||
read_trigger,
|
||||
_read_future,
|
||||
}),
|
||||
);
|
||||
self.respond(Response::AddPollable { id: Ok(id) });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_remove_pollable(self: &Rc<Self>, id: PollableId) {
|
||||
self.pollables.remove(&id);
|
||||
}
|
||||
|
||||
pub(super) fn handle_add_interest(
|
||||
self: &Rc<Self>,
|
||||
id: PollableId,
|
||||
writable: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let Some(pollable) = self.pollables.get(&id) else {
|
||||
return Err(CphError::PollableDoesNotExist);
|
||||
};
|
||||
let trigger = match writable {
|
||||
true => &pollable.write_trigger,
|
||||
false => &pollable.read_trigger,
|
||||
};
|
||||
trigger.trigger();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
102
src/config/handler/runtime.rs
Normal file
102
src/config/handler/runtime.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn handle_log_request(
|
||||
&self,
|
||||
level: ConfigLogLevel,
|
||||
msg: &str,
|
||||
file: Option<&str>,
|
||||
line: Option<u32>,
|
||||
) {
|
||||
let level = match level {
|
||||
ConfigLogLevel::Error => Level::Error,
|
||||
ConfigLogLevel::Warn => Level::Warn,
|
||||
ConfigLogLevel::Info => Level::Info,
|
||||
ConfigLogLevel::Debug => Level::Debug,
|
||||
ConfigLogLevel::Trace => Level::Trace,
|
||||
};
|
||||
let debug = fmt::from_fn(|fmt| {
|
||||
if let Some(file) = file {
|
||||
write!(fmt, "{}", file)?;
|
||||
if let Some(line) = line {
|
||||
write!(fmt, ":{}", line)?;
|
||||
}
|
||||
write!(fmt, ": ")?;
|
||||
}
|
||||
write!(fmt, "{}", msg)?;
|
||||
Ok(())
|
||||
});
|
||||
log::log!(level, "{:?}", debug);
|
||||
}
|
||||
|
||||
pub(super) fn handle_reload(&self) {
|
||||
self.state.reload_config();
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_status(&self, status: &str) {
|
||||
self.state.set_status(status);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_env(&self, key: &str, val: &str) {
|
||||
if let Some(f) = self.state.forker.get() {
|
||||
f.setenv(key.as_bytes(), val.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_unset_env(&self, key: &str) {
|
||||
if let Some(f) = self.state.forker.get() {
|
||||
f.unsetenv(key.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_config_dir(&self) {
|
||||
let dir = self.state.config_dir.clone().unwrap_or_default();
|
||||
self.respond(Response::GetConfigDir { dir });
|
||||
}
|
||||
|
||||
pub(super) fn handle_run(
|
||||
&self,
|
||||
prog: &str,
|
||||
args: Vec<String>,
|
||||
mut env: Vec<(String, String)>,
|
||||
fds: Vec<(i32, i32)>,
|
||||
tag: Option<&str>,
|
||||
) -> Result<(), CphError> {
|
||||
if let Some(tag) = tag {
|
||||
let display = self
|
||||
.state
|
||||
.tagged_acceptors
|
||||
.get(&self.state, tag)
|
||||
.map_err(CphError::CreateTaggedAcceptor)?;
|
||||
env.push((WAYLAND_DISPLAY.to_string(), display.to_string()));
|
||||
}
|
||||
let fds: Vec<_> = fds
|
||||
.into_iter()
|
||||
.map(|(a, b)| (a, Rc::new(OwnedFd::new(b))))
|
||||
.collect();
|
||||
let forker = match self.state.forker.get() {
|
||||
Some(f) => f,
|
||||
_ => return Err(CphError::NoForker),
|
||||
};
|
||||
let env = env.into_iter().map(|(k, v)| (k, Some(v))).collect();
|
||||
forker.spawn(prog.to_string(), args, env, fds);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_log_level(&self, level: ConfigLogLevel) {
|
||||
self.state.set_log_level(level.into());
|
||||
}
|
||||
|
||||
pub(super) fn handle_clean_logs_older_than(&self, time: SystemTime) {
|
||||
self.state.clean_logs_older_than.set(Some(time));
|
||||
}
|
||||
|
||||
pub(super) fn handle_quit(&self) {
|
||||
log::info!("Quitting");
|
||||
self.state.ring.stop();
|
||||
}
|
||||
|
||||
pub(super) fn handle_switch_to(&self, vtnr: u32) {
|
||||
self.state.backend.get().switch_to(vtnr);
|
||||
}
|
||||
}
|
||||
553
src/config/handler/seats.rs
Normal file
553
src/config/handler/seats.rs
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn handle_get_seat(&self, name: &str) {
|
||||
for seat in self.state.globals.seats.lock().values() {
|
||||
if seat.seat_name() == name {
|
||||
self.respond(Response::GetSeat {
|
||||
seat: Seat(seat.id().raw() as _),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
let seat = self.state.create_seat(name);
|
||||
self.respond(Response::GetSeat {
|
||||
seat: Seat(seat.id().raw() as _),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_fullscreen(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetFullscreen {
|
||||
fullscreen: seat.get_fullscreen(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat_fullscreen(
|
||||
&self,
|
||||
seat: Seat,
|
||||
fullscreen: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_fullscreen(fullscreen);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_keymap(
|
||||
&self,
|
||||
seat: Seat,
|
||||
keymap: Keymap,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let keymap = if keymap.is_invalid() {
|
||||
self.state.default_keymap.clone()
|
||||
} else {
|
||||
self.get_keymap(keymap)?
|
||||
};
|
||||
seat.set_seat_keymap(&keymap);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_forward(&self, seat: Seat, forward: bool) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_forward(forward);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_focus_follows_mouse_mode(
|
||||
&self,
|
||||
seat: Seat,
|
||||
mode: FocusFollowsMouseMode,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let focus_follows_mouse = match mode {
|
||||
FocusFollowsMouseMode::True => true,
|
||||
FocusFollowsMouseMode::False => false,
|
||||
};
|
||||
seat.set_focus_follows_mouse(focus_follows_mouse);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_fallback_output_mode(
|
||||
&self,
|
||||
seat: Seat,
|
||||
mode: FallbackOutputMode,
|
||||
) -> Result<(), CphError> {
|
||||
let Ok(mode) = mode.try_into() else {
|
||||
return Err(CphError::UnknownFallbackOutputMode(mode));
|
||||
};
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_fallback_output_mode(mode);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_management_enabled(
|
||||
&self,
|
||||
seat: Seat,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_window_management_enabled(enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_close(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus(
|
||||
&self,
|
||||
seat: Seat,
|
||||
direction: Direction,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_focus(direction.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_move(
|
||||
&self,
|
||||
seat: Seat,
|
||||
direction: Direction,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_focused(direction.into());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_send_to_scratchpad(
|
||||
&self,
|
||||
seat: Seat,
|
||||
name: &str,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if let Some(toplevel) = seat.get_keyboard_node().node_toplevel() {
|
||||
self.state.send_to_scratchpad(name, toplevel);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_toggle_scratchpad(
|
||||
&self,
|
||||
seat: Seat,
|
||||
name: &str,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.state.toggle_scratchpad(&seat, name);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_cycle_scratchpad(
|
||||
&self,
|
||||
seat: Seat,
|
||||
name: &str,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.state.cycle_scratchpad(&seat, name);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_repeat_rate(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let (rate, delay) = seat.get_rate();
|
||||
self.respond(Response::GetRepeatRate { rate, delay });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_repeat_rate(
|
||||
&self,
|
||||
seat: Seat,
|
||||
rate: i32,
|
||||
delay: i32,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if rate < 0 {
|
||||
return Err(CphError::NegativeRepeatRate);
|
||||
}
|
||||
if delay < 0 {
|
||||
return Err(CphError::NegativeRepeatDelay);
|
||||
}
|
||||
seat.set_rate(rate, delay);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_cursor_size(&self, seat: Seat, size: i32) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if size < 0 {
|
||||
return Err(CphError::NegativeCursorSize);
|
||||
}
|
||||
seat.cursor_group().set_cursor_size(size as _);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_disable_pointer_constraint(
|
||||
&self,
|
||||
seat: Seat,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.disable_pointer_constraint();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_use_hardware_cursor(
|
||||
&self,
|
||||
seat: Seat,
|
||||
use_hardware_cursor: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.cursor_group().set_hardware_cursor(use_hardware_cursor);
|
||||
self.state.refresh_hardware_cursors();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_float_pinned(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetFloatPinned {
|
||||
pinned: seat.pinned(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat_float_pinned(
|
||||
&self,
|
||||
seat: Seat,
|
||||
pinned: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_pinned(pinned);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_mono(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetMono {
|
||||
mono: seat.get_mono().unwrap_or(false),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat_mono(&self, seat: Seat, mono: bool) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_mono(mono);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_split(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetSplit {
|
||||
axis: seat
|
||||
.get_split()
|
||||
.unwrap_or(ContainerSplit::Horizontal)
|
||||
.into(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat_split(&self, seat: Seat, axis: Axis) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_split(axis.into());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_toggle_tab(&self, seat: Seat) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.toggle_tab();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_make_group(
|
||||
&self,
|
||||
seat: Seat,
|
||||
axis: Axis,
|
||||
ephemeral: bool,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.make_group(axis.into(), ephemeral);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_change_group_opposite(&self, seat: Seat) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.change_group_opposite();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_equalize(
|
||||
&self,
|
||||
seat: Seat,
|
||||
recursive: bool,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.equalize(recursive);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_move_tab(&self, seat: Seat, right: bool) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_tab(right);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_add_shortcut(
|
||||
&self,
|
||||
seat: Seat,
|
||||
mod_mask: Modifiers,
|
||||
mods: Modifiers,
|
||||
sym: KeySym,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.add_shortcut(mod_mask, mods, sym);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_remove_shortcut(
|
||||
&self,
|
||||
seat: Seat,
|
||||
mods: Modifiers,
|
||||
sym: KeySym,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.remove_shortcut(mods, sym);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seats(&self) {
|
||||
let seats = {
|
||||
let seats = self.state.globals.seats.lock();
|
||||
seats
|
||||
.values()
|
||||
.map(|seat| Seat::from_raw(seat.id().raw() as _))
|
||||
.collect()
|
||||
};
|
||||
self.respond(Response::GetSeats { seats });
|
||||
}
|
||||
|
||||
pub(super) fn handle_create_seat_split(
|
||||
&self,
|
||||
seat: Seat,
|
||||
axis: Axis,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.create_split(axis.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_focus_seat_parent(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_parent();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_floating(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetFloating {
|
||||
floating: seat.get_floating().unwrap_or(false),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat_floating(
|
||||
&self,
|
||||
seat: Seat,
|
||||
floating: bool,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_floating(floating);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_pointer_revert_key(
|
||||
&self,
|
||||
seat: Seat,
|
||||
key: KeySym,
|
||||
) -> Result<(), CphError> {
|
||||
self.get_seat(seat)?.set_pointer_revert_key(key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_history(
|
||||
&self,
|
||||
seat: Seat,
|
||||
timeline: Timeline,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
match timeline {
|
||||
Timeline::Older => seat.focus_prev(),
|
||||
Timeline::Newer => seat.focus_next(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_history_set_only_visible(
|
||||
&self,
|
||||
seat: Seat,
|
||||
visible: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_history_set_visible(visible);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_history_set_same_workspace(
|
||||
&self,
|
||||
seat: Seat,
|
||||
same_workspace: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_history_set_same_workspace(same_workspace);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_layer_rel(
|
||||
&self,
|
||||
seat: Seat,
|
||||
direction: LayerDirection,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
match direction {
|
||||
LayerDirection::Below => seat.focus_layer_below(),
|
||||
LayerDirection::Above => seat.focus_layer_above(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_tiles(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_tiles();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_floats(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_floats();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_toggle_focus_float_tiled(
|
||||
&self,
|
||||
seat: Seat,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.toggle_focus_float_tiled();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_middle_click_paste_enabled(&self, enabled: bool) {
|
||||
self.state.set_primary_selection_enabled(enabled);
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_create_mark(
|
||||
&self,
|
||||
seat: Seat,
|
||||
kc: Option<u32>,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if let Some(kc) = kc {
|
||||
seat.create_mark(Keycode::from_evdev(kc));
|
||||
} else {
|
||||
seat.create_mark_interactive();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_jump_to_mark(
|
||||
&self,
|
||||
seat: Seat,
|
||||
kc: Option<u32>,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if let Some(kc) = kc {
|
||||
seat.jump_to_mark(Keycode::from_evdev(kc));
|
||||
} else {
|
||||
seat.jump_to_mark_interactive();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_copy_mark(
|
||||
&self,
|
||||
seat: Seat,
|
||||
src: u32,
|
||||
dst: u32,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.copy_mark(Keycode::from_evdev(src), Keycode::from_evdev(dst));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_set_simple_im_enabled(
|
||||
&self,
|
||||
seat: Seat,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_simple_im_enabled(enabled);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_get_simple_im_enabled(
|
||||
&self,
|
||||
seat: Seat,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::SeatGetSimpleImEnabled {
|
||||
enabled: seat.simple_im_enabled(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_reload_simple_im(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.reload_simple_im();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_enable_unicode_input(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.enable_unicode_input();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_warp_mouse_to_focus(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.schedule_warp_mouse_to_focus();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_set_mouse_follows_focus(
|
||||
&self,
|
||||
seat: Seat,
|
||||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.set_mouse_follows_focus(enabled);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
200
src/config/handler/theme.rs
Normal file
200
src/config/handler/theme.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn handle_set_float_above_fullscreen(&self, above: bool) {
|
||||
self.state.set_float_above_fullscreen(above);
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_float_above_fullscreen(&self) {
|
||||
self.respond(Response::GetFloatAboveFullscreen {
|
||||
above: self.state.float_above_fullscreen.get(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_show_bar(&self, show: bool) {
|
||||
self.state.set_show_bar(show);
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_show_bar(&self) {
|
||||
self.respond(Response::GetShowBar {
|
||||
show: self.state.show_bar.get(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_show_titles(&self, _show: bool) {
|
||||
// no-op: titles have been removed
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_show_titles(&self) {
|
||||
self.respond(Response::GetShowTitles { show: false });
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_floating_titles(&self, _floating: bool) {
|
||||
// no-op: titles have been removed
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_floating_titles(&self) {
|
||||
self.respond(Response::GetFloatingTitles { floating: false });
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_bar_position(
|
||||
&self,
|
||||
position: BarPosition,
|
||||
) -> Result<(), CphError> {
|
||||
let Ok(position) = position.try_into() else {
|
||||
return Err(CphError::UnknownBarPosition(position));
|
||||
};
|
||||
self.state.set_bar_position(position);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_bar_position(&self) {
|
||||
self.respond(Response::GetBarPosition {
|
||||
position: self.state.theme.bar_position.get().into(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_corner_radius(&self, radius: f32) {
|
||||
use crate::theme::CornerRadius;
|
||||
let radius = radius.max(0.0).min(1000.0);
|
||||
self.state
|
||||
.theme
|
||||
.corner_radius
|
||||
.set(CornerRadius::from(radius));
|
||||
self.state.damage(self.state.root.extents.get());
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_corner_radius(&self) {
|
||||
self.respond(Response::GetCornerRadius {
|
||||
radius: self.state.theme.corner_radius.get().top_left,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_show_float_pin_icon(&self, _show: bool) {
|
||||
// no-op: titles have been removed, pin icon was in title bar
|
||||
}
|
||||
|
||||
fn get_sized(&self, sized: Resizable) -> Result<ThemeSized, CphError> {
|
||||
use jay_config::theme::sized::*;
|
||||
let sized = match sized {
|
||||
TITLE_HEIGHT => ThemeSized::title_height,
|
||||
BORDER_WIDTH => ThemeSized::border_width,
|
||||
BAR_HEIGHT => ThemeSized::bar_height,
|
||||
BAR_SEPARATOR_WIDTH => ThemeSized::bar_separator_width,
|
||||
GAP => ThemeSized::gap,
|
||||
TITLE_GAP => ThemeSized::title_gap,
|
||||
TAB_BAR_HEIGHT => ThemeSized::tab_bar_height,
|
||||
TAB_BAR_PADDING => ThemeSized::tab_bar_padding,
|
||||
TAB_BAR_RADIUS => ThemeSized::tab_bar_radius,
|
||||
TAB_BAR_BORDER_WIDTH => ThemeSized::tab_bar_border_width,
|
||||
TAB_BAR_TEXT_PADDING => ThemeSized::tab_bar_text_padding,
|
||||
TAB_BAR_GAP => ThemeSized::tab_bar_gap,
|
||||
_ => return Err(CphError::UnknownSized(sized.0)),
|
||||
};
|
||||
Ok(sized)
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_size(&self, sized: Resizable) -> Result<(), CphError> {
|
||||
let sized = self.get_sized(sized)?;
|
||||
let size = sized.field(&self.state.theme).val.get();
|
||||
self.respond(Response::GetSize { size });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_size(&self, sized: Resizable, size: i32) -> Result<(), CphError> {
|
||||
let sized = self.get_sized(sized)?;
|
||||
if size < sized.min() {
|
||||
return Err(CphError::InvalidSize(size, sized));
|
||||
}
|
||||
if size > sized.max() {
|
||||
return Err(CphError::InvalidSize(size, sized));
|
||||
}
|
||||
self.state.set_size(sized, size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_reset_colors(&self) {
|
||||
self.state.reset_colors();
|
||||
}
|
||||
|
||||
pub(super) fn handle_reset_sizes(&self) {
|
||||
self.state.reset_sizes();
|
||||
}
|
||||
|
||||
pub(super) fn handle_reset_font(&self) {
|
||||
self.state.reset_fonts();
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_font(&self, font: &str) {
|
||||
self.state.set_font(font);
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_bar_font(&self, font: &str) {
|
||||
self.state.set_bar_font(Some(font));
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_title_font(&self, _font: &str) {
|
||||
// no-op: titles have been removed
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_font(&self) {
|
||||
let font = self.state.theme.font.get().to_string();
|
||||
self.respond(Response::GetFont { font });
|
||||
}
|
||||
|
||||
fn get_color(&self, colorable: Colorable) -> Result<ThemeColor, CphError> {
|
||||
use jay_config::theme::colors::*;
|
||||
let colorable = match colorable {
|
||||
UNFOCUSED_TITLE_BACKGROUND_COLOR => ThemeColor::unfocused_title_background,
|
||||
FOCUSED_TITLE_BACKGROUND_COLOR => ThemeColor::focused_title_background,
|
||||
CAPTURED_UNFOCUSED_TITLE_BACKGROUND_COLOR => {
|
||||
ThemeColor::captured_unfocused_title_background
|
||||
}
|
||||
CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR => {
|
||||
ThemeColor::captured_focused_title_background
|
||||
}
|
||||
FOCUSED_INACTIVE_TITLE_BACKGROUND_COLOR => {
|
||||
ThemeColor::focused_inactive_title_background
|
||||
}
|
||||
BACKGROUND_COLOR => ThemeColor::background,
|
||||
BAR_BACKGROUND_COLOR => ThemeColor::bar_background,
|
||||
SEPARATOR_COLOR => ThemeColor::separator,
|
||||
BORDER_COLOR => ThemeColor::border,
|
||||
ACTIVE_BORDER_COLOR => ThemeColor::active_border,
|
||||
UNFOCUSED_TITLE_TEXT_COLOR => ThemeColor::unfocused_title_text,
|
||||
FOCUSED_TITLE_TEXT_COLOR => ThemeColor::focused_title_text,
|
||||
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => ThemeColor::focused_inactive_title_text,
|
||||
BAR_STATUS_TEXT_COLOR => ThemeColor::bar_text,
|
||||
ATTENTION_REQUESTED_BACKGROUND_COLOR => ThemeColor::attention_requested_background,
|
||||
HIGHLIGHT_COLOR => ThemeColor::highlight,
|
||||
TAB_ACTIVE_BACKGROUND_COLOR => ThemeColor::tab_active_background,
|
||||
TAB_ACTIVE_BORDER_COLOR => ThemeColor::tab_active_border,
|
||||
TAB_INACTIVE_BACKGROUND_COLOR => ThemeColor::tab_inactive_background,
|
||||
TAB_INACTIVE_BORDER_COLOR => ThemeColor::tab_inactive_border,
|
||||
TAB_ACTIVE_TEXT_COLOR => ThemeColor::tab_active_text,
|
||||
TAB_INACTIVE_TEXT_COLOR => ThemeColor::tab_inactive_text,
|
||||
TAB_BAR_BACKGROUND_COLOR => ThemeColor::tab_bar_background,
|
||||
TAB_ATTENTION_BACKGROUND_COLOR => ThemeColor::tab_attention_background,
|
||||
_ => return Err(CphError::UnknownColor(colorable.0)),
|
||||
};
|
||||
Ok(colorable)
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_color(&self, colorable: Colorable) -> Result<(), CphError> {
|
||||
let color = self.get_color(colorable)?.field(&self.state.theme).get();
|
||||
let [r, g, b, a] = color.to_array(Eotf::Gamma22);
|
||||
let color = jay_config::theme::Color::new_f32_premultiplied(r, g, b, a);
|
||||
self.respond(Response::GetColor { color });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_color(
|
||||
&self,
|
||||
colorable: Colorable,
|
||||
color: jay_config::theme::Color,
|
||||
) -> Result<(), CphError> {
|
||||
self.state
|
||||
.set_color(self.get_color(colorable)?, color.into());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
355
src/config/handler/windows.rs
Normal file
355
src/config/handler/windows.rs
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn tl_to_window(&self, tl: &dyn ToplevelNode) -> Window {
|
||||
self.tl_id_to_window(tl.tl_data().identifier.get())
|
||||
}
|
||||
|
||||
pub(super) fn tl_id_to_window(&self, tl: ToplevelIdentifier) -> Window {
|
||||
if let Some(win) = self.windows_from_tl_id.get(&tl) {
|
||||
return win;
|
||||
}
|
||||
let id = Window(self.window_ids.fetch_add(1));
|
||||
self.windows_from_tl_id.set(tl, id);
|
||||
self.windows_to_tl_id.set(id, tl);
|
||||
id
|
||||
}
|
||||
|
||||
pub(super) fn get_window(&self, window: Window) -> Result<Rc<dyn ToplevelNode>, CphError> {
|
||||
self.windows_to_tl_id
|
||||
.get(&window)
|
||||
.and_then(|id| self.state.toplevels.get(&id))
|
||||
.and_then(|tl| tl.upgrade())
|
||||
.ok_or(CphError::WindowDoesNotExist(window))
|
||||
}
|
||||
pub(super) fn get_client(&self, client: ConfigClient) -> Result<Rc<Client>, CphError> {
|
||||
self.state
|
||||
.clients
|
||||
.get(ClientId::from_raw(client.0))
|
||||
.ok()
|
||||
.ok_or(CphError::ClientDoesNotExist(client))
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_clients(&self) {
|
||||
let mut clients = vec![];
|
||||
for client in self.state.clients.clients.borrow().values() {
|
||||
clients.push(ConfigClient(client.data.id.raw()));
|
||||
}
|
||||
self.respond(Response::GetClients { clients });
|
||||
}
|
||||
|
||||
pub(super) fn handle_client_exists(&self, client: ConfigClient) {
|
||||
self.respond(Response::ClientExists {
|
||||
exists: self.get_client(client).is_ok(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_client_is_xwayland(&self, client: ConfigClient) -> Result<(), CphError> {
|
||||
self.respond(Response::ClientIsXwayland {
|
||||
is_xwayland: self.get_client(client)?.is_xwayland,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_client_kill(&self, client: ConfigClient) {
|
||||
self.state.clients.kill(ClientId::from_raw(client.0));
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_workspace_window(&self, ws: Workspace) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_existing_workspace(ws)?
|
||||
.and_then(|ws| ws.container.get())
|
||||
.map(|c| self.tl_to_window(&*c))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetWorkspaceWindow { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_keyboard_window(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_seat(seat)?
|
||||
.get_keyboard_node()
|
||||
.node_toplevel()
|
||||
.map(|tl| self.tl_to_window(&*tl))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetSeatKeyboardWindow { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_seat_focus_window(&self, seat: Seat, window_id: Window) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let window = self.get_window(window_id)?;
|
||||
if !window.node_visible() {
|
||||
return Err(CphError::WindowNotVisible(window_id));
|
||||
}
|
||||
seat.focus_toplevel(window);
|
||||
seat.maybe_schedule_warp_mouse_to_focus();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_title(&self, window: Window) -> Result<(), CphError> {
|
||||
let title = self.get_window(window)?.tl_data().title.borrow().clone();
|
||||
self.respond(Response::GetWindowTitle { title });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_type(&self, window: Window) -> Result<(), CphError> {
|
||||
let kind = self.get_window(window)?.tl_data().kind.to_window_type();
|
||||
self.respond(Response::GetWindowType { kind });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_content_type(&self, window: Window) -> Result<(), CphError> {
|
||||
let kind = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.content_type
|
||||
.get()
|
||||
.to_config();
|
||||
self.respond(Response::GetContentType { kind });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_window_resize(
|
||||
&self,
|
||||
window: Window,
|
||||
dx1: i32,
|
||||
dy1: i32,
|
||||
dx2: i32,
|
||||
dy2: i32,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
self.get_window(window)?.tl_resize(dx1, dy1, dx2, dy2);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_window_send_to_scratchpad(
|
||||
&self,
|
||||
window: Window,
|
||||
name: &str,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
self.state.send_to_scratchpad(name, window);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_window_exists(&self, window: Window) {
|
||||
self.respond(Response::WindowExists {
|
||||
exists: self.get_window(window).is_ok(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_id(&self, window: Window) -> Result<(), CphError> {
|
||||
let id = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.identifier
|
||||
.get()
|
||||
.to_string();
|
||||
self.respond(Response::GetWindowId { id: id.to_string() });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_is_visible(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowIsVisible {
|
||||
visible: window.node_visible(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_client(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowClient {
|
||||
client: window
|
||||
.tl_data()
|
||||
.client
|
||||
.as_ref()
|
||||
.map(|c| ConfigClient(c.id.raw()))
|
||||
.unwrap_or(ConfigClient(0)),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_parent(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|tl| tl.node_into_toplevel())
|
||||
.map(|tl| self.tl_to_window(&*tl))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetWindowParent { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_workspace(&self, window: Window) -> Result<(), CphError> {
|
||||
let workspace = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.workspace
|
||||
.get()
|
||||
.map(|ws| self.get_workspace_by_name(&ws.name))
|
||||
.unwrap_or(Workspace(0));
|
||||
self.respond(Response::GetWindowWorkspace { workspace });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_children(&self, window: Window) -> Result<(), CphError> {
|
||||
let mut windows = vec![];
|
||||
if let Some(c) = self.get_window(window)?.node_into_container() {
|
||||
for c in c.children.iter() {
|
||||
windows.push(self.tl_to_window(&*c.node));
|
||||
}
|
||||
}
|
||||
self.respond(Response::GetWindowChildren { windows });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_window_close(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
window.tl_close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_window_move(
|
||||
&self,
|
||||
window: Window,
|
||||
direction: Direction,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(float) = window.tl_data().float.get() {
|
||||
float.move_by_direction(direction.into());
|
||||
} else if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.move_child(window, direction.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_fullscreen(
|
||||
&self,
|
||||
window: Window,
|
||||
) -> Result<(), CphError> {
|
||||
let tl = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFullscreen {
|
||||
fullscreen: tl.tl_data().is_fullscreen.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_fullscreen(
|
||||
&self,
|
||||
window: Window,
|
||||
fullscreen: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let tl = self.get_window(window)?;
|
||||
tl.tl_set_fullscreen(fullscreen, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_float_pinned(
|
||||
&self,
|
||||
window: Window,
|
||||
) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFloatPinned {
|
||||
pinned: window.tl_pinned(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_float_pinned(
|
||||
&self,
|
||||
window: Window,
|
||||
pinned: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
window.tl_set_pinned(true, pinned);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_mono(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowMono {
|
||||
mono: toplevel_parent_container(&*window)
|
||||
.map(|c| c.mono_child.is_some())
|
||||
.unwrap_or(false),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_mono(
|
||||
&self,
|
||||
window: Window,
|
||||
mono: bool,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_mono(mono.then_some(window.as_ref()));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowSplit {
|
||||
axis: toplevel_parent_container(&*window)
|
||||
.map(|c| c.split.get())
|
||||
.unwrap_or(ContainerSplit::Horizontal)
|
||||
.into(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_split(
|
||||
&self,
|
||||
window: Window,
|
||||
axis: Axis,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_split(axis.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_create_window_split(
|
||||
&self,
|
||||
window: Window,
|
||||
axis: Axis,
|
||||
) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_create_split(&self.state, window, axis.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_window_floating(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFloating {
|
||||
floating: window.tl_data().parent_is_float.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_floating(
|
||||
&self,
|
||||
window: Window,
|
||||
floating: bool,
|
||||
) -> Result<(), CphError> {
|
||||
self.state.with_linear_layout_animations(|| {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_set_floating(&self.state, window, floating);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
186
src/config/handler/workspaces.rs
Normal file
186
src/config/handler/workspaces.rs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
use super::*;
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub(super) fn get_workspace_by_name(&self, name: &String) -> Workspace {
|
||||
let id = match self.workspaces_by_name.get(name) {
|
||||
None => {
|
||||
let id = self.workspace_ids.fetch_add(1);
|
||||
let name = Rc::new(name.clone());
|
||||
self.workspaces_by_name.set(name.clone(), id);
|
||||
self.workspaces_by_id.set(id, name);
|
||||
id
|
||||
}
|
||||
Some(id) => id,
|
||||
};
|
||||
Workspace(id)
|
||||
}
|
||||
|
||||
pub(super) fn get_workspace(&self, ws: Workspace) -> Result<Rc<String>, CphError> {
|
||||
match self.workspaces_by_id.get(&ws.0) {
|
||||
Some(ws) => Ok(ws),
|
||||
_ => Err(CphError::WorkspaceDoesNotExist(ws)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_existing_workspace(
|
||||
&self,
|
||||
ws: Workspace,
|
||||
) -> Result<Option<Rc<WorkspaceNode>>, CphError> {
|
||||
self.get_workspace(ws).map(|name| {
|
||||
self.state
|
||||
.workspaces
|
||||
.lock()
|
||||
.values()
|
||||
.find(|ws| ws.name.as_str() == name.as_str())
|
||||
.cloned()
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_workspaces(&self) {
|
||||
let mut workspaces = vec![];
|
||||
for ws in self.state.workspaces.lock().values() {
|
||||
workspaces.push(self.get_workspace_by_name(&ws.name));
|
||||
}
|
||||
self.respond(Response::GetWorkspaces { workspaces });
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_workspace(&self, name: &str) {
|
||||
self.respond(Response::GetWorkspace {
|
||||
workspace: self.get_workspace_by_name(&name.to_owned()),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_workspace_capture(
|
||||
&self,
|
||||
workspace: Workspace,
|
||||
) -> Result<(), CphError> {
|
||||
let ws = self.get_existing_workspace(workspace)?;
|
||||
let capture = match ws {
|
||||
Some(ws) => ws.may_capture.get(),
|
||||
None => self.state.default_workspace_capture.get(),
|
||||
};
|
||||
self.respond(Response::GetWorkspaceCapture { capture });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_workspace_capture(
|
||||
&self,
|
||||
workspace: Workspace,
|
||||
capture: bool,
|
||||
) -> Result<(), CphError> {
|
||||
if let Some(ws) = self.get_existing_workspace(workspace)? {
|
||||
ws.may_capture.set(capture);
|
||||
ws.update_has_captures();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_default_workspace_capture(&self) {
|
||||
self.respond(Response::GetDefaultWorkspaceCapture {
|
||||
capture: self.state.default_workspace_capture.get(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_default_workspace_capture(&self, capture: bool) {
|
||||
self.state.default_workspace_capture.set(capture);
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_cursor_workspace(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let output = seat.get_cursor_output();
|
||||
let mut workspace = Workspace(0);
|
||||
if !output.is_dummy
|
||||
&& let Some(ws) = output.workspace.get()
|
||||
{
|
||||
workspace = self.get_workspace_by_name(&ws.name);
|
||||
}
|
||||
self.respond(Response::GetSeatCursorWorkspace { workspace });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_get_seat_keyboard_workspace(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let mut workspace = Workspace(0);
|
||||
if let Some(output) = seat.get_keyboard_output()
|
||||
&& !output.is_dummy
|
||||
&& let Some(ws) = output.workspace.get()
|
||||
{
|
||||
workspace = self.get_workspace_by_name(&ws.name);
|
||||
}
|
||||
self.respond(Response::GetSeatKeyboardWorkspace { workspace });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_show_workspace(
|
||||
&self,
|
||||
seat: Seat,
|
||||
ws: Workspace,
|
||||
output: Option<Connector>,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let name = self.get_workspace(ws)?;
|
||||
let output = output.map(|o| self.get_output_node(o)).transpose()?;
|
||||
self.state.show_workspace(&seat, &name, output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_seat_workspace(
|
||||
&self,
|
||||
seat: Seat,
|
||||
ws: Workspace,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let name = self.get_workspace(ws)?;
|
||||
let output = seat.get_fallback_output();
|
||||
let workspace = match output.find_workspace(name.deref()) {
|
||||
Some(ws) => ws,
|
||||
_ => output.create_workspace(name.deref()),
|
||||
};
|
||||
seat.set_workspace(&workspace);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_window_workspace(
|
||||
&self,
|
||||
window: Window,
|
||||
ws: Workspace,
|
||||
) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
let name = self.get_workspace(ws)?;
|
||||
let Some(output) = window.node_output() else {
|
||||
return Ok(());
|
||||
};
|
||||
let workspace = match output.find_workspace(name.deref()) {
|
||||
Some(ws) => ws,
|
||||
_ => output.create_workspace(name.deref()),
|
||||
};
|
||||
toplevel_set_workspace(&self.state, window, &workspace);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_move_to_output(
|
||||
&self,
|
||||
workspace: WorkspaceSource,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let output = self.get_output_node(connector)?;
|
||||
let ws = match workspace {
|
||||
WorkspaceSource::Explicit(ws) => match self.get_existing_workspace(ws)? {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
},
|
||||
WorkspaceSource::Seat(s) => {
|
||||
match self.get_seat(s)?.get_fallback_output().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.state.move_ws_to_output(&ws, &output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn handle_set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
||||
self.state.set_workspace_display_order(order.into());
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,8 @@ use {
|
|||
format::{FORMATS, Format},
|
||||
gfx_api::FdSync,
|
||||
io_uring::IoUring,
|
||||
rect::{Rect, Region},
|
||||
utils::{
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
numcell::NumCell,
|
||||
oserror::{OsError, OsErrorExt2},
|
||||
|
|
@ -22,12 +20,10 @@ use {
|
|||
},
|
||||
vulkan_core::{
|
||||
self, VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, device::VulkanDeviceInf,
|
||||
map_extension_properties, sync::VulkanDeviceSyncExt,
|
||||
timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
|
||||
map_extension_properties, timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
|
||||
},
|
||||
},
|
||||
ahash::{AHashMap, AHashSet},
|
||||
arrayvec::ArrayVec,
|
||||
ahash::AHashMap,
|
||||
ash::{
|
||||
Device,
|
||||
ext::{
|
||||
|
|
@ -36,12 +32,9 @@ use {
|
|||
},
|
||||
khr::{external_fence_fd, external_memory_fd, external_semaphore_fd},
|
||||
vk::{
|
||||
self, AccessFlags2, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BlitImageInfo2,
|
||||
BufferCopy2, BufferCreateInfo, BufferImageCopy2, BufferMemoryBarrier2,
|
||||
BufferUsageFlags, CommandBuffer, CommandBufferAllocateInfo, CommandBufferBeginInfo,
|
||||
CommandBufferSubmitInfo, CommandBufferUsageFlags, CommandPoolCreateFlags,
|
||||
CommandPoolCreateInfo, CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2,
|
||||
CopyImageToBufferInfo2, DependencyInfo, DeviceCreateInfo, DeviceMemory,
|
||||
self, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BufferCopy2, BufferCreateInfo,
|
||||
BufferImageCopy2, BufferUsageFlags, CommandBuffer, CommandBufferAllocateInfo,
|
||||
CommandPoolCreateFlags, CommandPoolCreateInfo, DeviceCreateInfo, DeviceMemory,
|
||||
DeviceQueueCreateInfo, DrmFormatModifierPropertiesEXT,
|
||||
DrmFormatModifierPropertiesListEXT, ExportMemoryAllocateInfo, Extent3D,
|
||||
ExternalBufferProperties, ExternalFenceFeatureFlags, ExternalFenceHandleTypeFlags,
|
||||
|
|
@ -49,28 +42,26 @@ use {
|
|||
ExternalMemoryBufferCreateInfo, ExternalMemoryBufferCreateInfoKHR,
|
||||
ExternalMemoryFeatureFlags, ExternalMemoryHandleTypeFlags,
|
||||
ExternalMemoryImageCreateInfo, ExternalSemaphoreFeatureFlags,
|
||||
ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, Filter,
|
||||
FormatFeatureFlags, FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2,
|
||||
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
|
||||
ImageFormatProperties2, ImageLayout, ImageMemoryBarrier2, ImageMemoryRequirementsInfo2,
|
||||
ImagePlaneMemoryRequirementsInfo, ImageSubresourceLayers, ImageSubresourceRange,
|
||||
ImageTiling, ImageType, ImageUsageFlags, ImportMemoryFdInfoKHR,
|
||||
ImportSemaphoreFdInfoKHR, MemoryAllocateInfo, MemoryDedicatedAllocateInfo,
|
||||
MemoryFdPropertiesKHR, MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2,
|
||||
MemoryType, Offset3D, PhysicalDevice, PhysicalDeviceDrmPropertiesEXT,
|
||||
ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, FormatFeatureFlags,
|
||||
FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2, ImageCreateFlags,
|
||||
ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageFormatProperties2,
|
||||
ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
|
||||
ImageTiling, ImageType, ImageUsageFlags, ImportMemoryFdInfoKHR, ImportSemaphoreFdInfoKHR,
|
||||
MemoryAllocateInfo, MemoryDedicatedAllocateInfo, MemoryFdPropertiesKHR,
|
||||
MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2, MemoryType,
|
||||
PhysicalDevice, PhysicalDeviceDrmPropertiesEXT,
|
||||
PhysicalDeviceExternalBufferInfo, PhysicalDeviceExternalFenceInfo,
|
||||
PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo,
|
||||
PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT,
|
||||
PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2,
|
||||
PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures,
|
||||
PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, Queue, QueueFlags, SampleCountFlags,
|
||||
SemaphoreCreateInfo, SemaphoreImportFlags, SemaphoreSubmitInfo, SharingMode,
|
||||
SubmitInfo2, SubresourceLayout, WHOLE_SIZE,
|
||||
Queue, QueueFlags, SampleCountFlags, SemaphoreCreateInfo, SemaphoreImportFlags,
|
||||
SharingMode, SubresourceLayout,
|
||||
},
|
||||
},
|
||||
bstr::ByteSlice,
|
||||
isnt::std_1::collections::IsntHashMapExt,
|
||||
linearize::{Linearize, LinearizeExt, StaticCopyMap, StaticMap, static_copy_map, static_map},
|
||||
linearize::{Linearize, LinearizeExt, StaticCopyMap, StaticMap, static_map},
|
||||
log::Level,
|
||||
run_on_drop::on_drop,
|
||||
std::{
|
||||
|
|
@ -86,6 +77,14 @@ use {
|
|||
vk::{Buffer, CommandPool, Image, Semaphore},
|
||||
};
|
||||
|
||||
mod execute;
|
||||
mod queue_allocation;
|
||||
mod registry;
|
||||
|
||||
pub use registry::CopyDeviceRegistry;
|
||||
|
||||
use queue_allocation::{QueueIndex, QueueToAllocate, allocate_queues};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CopyDeviceError {
|
||||
#[error(transparent)]
|
||||
|
|
@ -197,20 +196,6 @@ pub struct PhysicalCopyDevice {
|
|||
image_blit_2: RefCell<Vec<ImageBlit2<'static>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct QueueToAllocate {
|
||||
family: u32,
|
||||
num: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
struct QueueIndex {
|
||||
allocate_idx: usize,
|
||||
family: u32,
|
||||
idx_within_family: u32,
|
||||
transfer_granularity_mask: (u32, u32),
|
||||
}
|
||||
|
||||
pub struct CopyDevice {
|
||||
_tasks: Vec<SpawnedFuture<()>>,
|
||||
dev: Rc<CopyDeviceInner>,
|
||||
|
|
@ -349,13 +334,6 @@ struct ClassifiedDmabuf<'a> {
|
|||
format: &'a CopyDeviceSupport,
|
||||
}
|
||||
|
||||
pub struct CopyDeviceRegistry {
|
||||
ring: Rc<IoUring>,
|
||||
eng: Rc<AsyncEngine>,
|
||||
eventfd_cache: Rc<EventfdCache>,
|
||||
devs: CopyHashMap<c::dev_t, Option<Rc<PhysicalCopyDevice>>>,
|
||||
}
|
||||
|
||||
const DEVICE_EXTENSIONS: [&CStr; 6] = [
|
||||
external_semaphore_fd::NAME,
|
||||
external_fence_fd::NAME,
|
||||
|
|
@ -1290,429 +1268,6 @@ impl CopyDeviceInner {
|
|||
}
|
||||
}
|
||||
|
||||
impl CopyDeviceCopy {
|
||||
fn ensure_not_busy(&self) -> Result<(), CopyDeviceError> {
|
||||
let slf = &*self.inner;
|
||||
if let Some(sync) = slf.busy.get()
|
||||
&& sync.is_unsignaled()
|
||||
{
|
||||
return Err(CopyDeviceError::Busy);
|
||||
}
|
||||
slf.busy.take();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn execute(
|
||||
&self,
|
||||
sync: Option<&FdSync>,
|
||||
region: Option<&Region>,
|
||||
) -> Result<Option<FdSync>, CopyDeviceError> {
|
||||
self.ensure_not_busy()?;
|
||||
let slf = &*self.inner;
|
||||
let tt = slf.tt;
|
||||
let dev = &slf.dev.dev;
|
||||
let cmd = slf.command_buffer;
|
||||
let queue_family = slf.dev.phy.queues[tt].family;
|
||||
let region_buf;
|
||||
let width = slf.width;
|
||||
let height = slf.height;
|
||||
let region = match region {
|
||||
Some(r) => r,
|
||||
_ => {
|
||||
region_buf = Region::new(Rect::new_saturating(0, 0, width as i32, height as i32));
|
||||
®ion_buf
|
||||
}
|
||||
};
|
||||
let (x_mask, y_mask) = slf.dev.phy.queues[tt].transfer_granularity_mask;
|
||||
let rects = &mut *slf.dev.phy.rects.borrow_mut();
|
||||
rects.clear();
|
||||
for rect in region.iter() {
|
||||
let x1 = (rect.x1().max(0) as u32 & !x_mask).min(width);
|
||||
let y1 = (rect.y1().max(0) as u32 & !y_mask).min(height);
|
||||
let x2 = ((rect.x2().max(0) as u32 + x_mask) & !x_mask).min(width);
|
||||
let y2 = ((rect.y2().max(0) as u32 + y_mask) & !y_mask).min(height);
|
||||
let width = x2 - x1;
|
||||
let height = y2 - y1;
|
||||
if width == 0 || height == 0 {
|
||||
continue;
|
||||
}
|
||||
rects.push((x1 as i32, y1 as i32, width, height));
|
||||
}
|
||||
if rects.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let begin_info =
|
||||
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||
unsafe {
|
||||
dev.begin_command_buffer(cmd, &begin_info)
|
||||
.map_err(CopyDeviceError::BeginCommandBuffer)?;
|
||||
}
|
||||
macro_rules! initial_buffer_barriers {
|
||||
($($buf:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
BufferMemoryBarrier2::default()
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.buffer($buf.buf)
|
||||
.size(WHOLE_SIZE),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
macro_rules! final_buffer_barriers {
|
||||
($($buf:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
BufferMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.buffer($buf.buf)
|
||||
.size(WHOLE_SIZE),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
let image_subresource_range = ImageSubresourceRange {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
};
|
||||
let image_subresource = ImageSubresourceLayers {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
mip_level: 0,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
};
|
||||
macro_rules! initial_image_barriers {
|
||||
($($img:expr, $layout:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
ImageMemoryBarrier2::default()
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.old_layout(ImageLayout::GENERAL)
|
||||
.new_layout(ImageLayout::GENERAL)
|
||||
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.old_layout(ImageLayout::GENERAL)
|
||||
.new_layout($layout)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
macro_rules! final_image_barriers {
|
||||
($($img:expr, $layout:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.old_layout($layout)
|
||||
.new_layout(ImageLayout::GENERAL)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.old_layout(ImageLayout::GENERAL)
|
||||
.new_layout(ImageLayout::GENERAL)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
match &slf.ty {
|
||||
CopyDeviceCopyType::BufferToBuffer {
|
||||
src,
|
||||
dst,
|
||||
stride,
|
||||
bpp,
|
||||
} => {
|
||||
let regions = &mut *slf.dev.phy.buffer_copy_2.borrow_mut();
|
||||
regions.clear();
|
||||
let stride = *stride as u64;
|
||||
let bpp = *bpp as u64;
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let lo = y as u64 * stride + x as u64 * bpp;
|
||||
let size = (height as u64 - 1) * stride + width as u64 * bpp;
|
||||
let region = BufferCopy2::default()
|
||||
.src_offset(lo)
|
||||
.dst_offset(lo)
|
||||
.size(size);
|
||||
regions.push(region);
|
||||
}
|
||||
use AccessFlags2 as A;
|
||||
let initial_barriers = initial_buffer_barriers![
|
||||
src, A::TRANSFER_READ;
|
||||
dst, A::TRANSFER_WRITE;
|
||||
];
|
||||
let final_barriers = final_buffer_barriers![
|
||||
src, A::TRANSFER_READ;
|
||||
dst, A::TRANSFER_WRITE;
|
||||
];
|
||||
let initial_dependency_info =
|
||||
DependencyInfo::default().buffer_memory_barriers(&initial_barriers);
|
||||
let final_dependency_info =
|
||||
DependencyInfo::default().buffer_memory_barriers(&final_barriers);
|
||||
let copy_buffer_info = CopyBufferInfo2::default()
|
||||
.src_buffer(src.buf)
|
||||
.dst_buffer(dst.buf)
|
||||
.regions(regions);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
dev.cmd_copy_buffer2(cmd, ©_buffer_info);
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
CopyDeviceCopyType::BufferToImage {
|
||||
buf,
|
||||
buf_format,
|
||||
buf_stride,
|
||||
img,
|
||||
}
|
||||
| CopyDeviceCopyType::ImageToBuffer {
|
||||
img,
|
||||
buf,
|
||||
buf_format,
|
||||
buf_stride,
|
||||
} => {
|
||||
let regions = &mut *slf.dev.phy.buffer_image_copy_2.borrow_mut();
|
||||
regions.clear();
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let offset = y as u64 * *buf_stride as u64 + x as u64 * buf_format.bpp as u64;
|
||||
let region = BufferImageCopy2::default()
|
||||
.buffer_offset(offset)
|
||||
.buffer_row_length(*buf_stride / buf_format.bpp)
|
||||
.buffer_image_height(slf.height)
|
||||
.image_subresource(image_subresource)
|
||||
.image_offset(Offset3D { x, y, z: 0 })
|
||||
.image_extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
});
|
||||
regions.push(region);
|
||||
}
|
||||
let buffer_to_image = match &slf.ty {
|
||||
CopyDeviceCopyType::BufferToImage { .. } => true,
|
||||
CopyDeviceCopyType::ImageToBuffer { .. } => false,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let image_access_mask;
|
||||
let image_layout;
|
||||
let buffer_access_mask;
|
||||
match buffer_to_image {
|
||||
true => {
|
||||
image_access_mask = AccessFlags2::TRANSFER_WRITE;
|
||||
image_layout = ImageLayout::TRANSFER_DST_OPTIMAL;
|
||||
buffer_access_mask = AccessFlags2::TRANSFER_READ;
|
||||
}
|
||||
false => {
|
||||
image_access_mask = AccessFlags2::TRANSFER_READ;
|
||||
image_layout = ImageLayout::TRANSFER_SRC_OPTIMAL;
|
||||
buffer_access_mask = AccessFlags2::TRANSFER_WRITE;
|
||||
}
|
||||
}
|
||||
let initial_image_barriers = initial_image_barriers![
|
||||
img, image_layout, image_access_mask;
|
||||
];
|
||||
let final_image_barriers = final_image_barriers![
|
||||
img, image_layout, image_access_mask;
|
||||
];
|
||||
let initial_buffer_barriers = initial_buffer_barriers![
|
||||
buf, buffer_access_mask;
|
||||
];
|
||||
let final_buffer_barriers = final_buffer_barriers![
|
||||
buf, buffer_access_mask;
|
||||
];
|
||||
let initial_dependency_info = DependencyInfo::default()
|
||||
.buffer_memory_barriers(&initial_buffer_barriers)
|
||||
.image_memory_barriers(&initial_image_barriers);
|
||||
let final_dependency_info = DependencyInfo::default()
|
||||
.buffer_memory_barriers(&final_buffer_barriers)
|
||||
.image_memory_barriers(&final_image_barriers);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
match buffer_to_image {
|
||||
true => {
|
||||
let copy = CopyBufferToImageInfo2::default()
|
||||
.src_buffer(buf.buf)
|
||||
.dst_image(img.img)
|
||||
.dst_image_layout(image_layout)
|
||||
.regions(®ions);
|
||||
dev.cmd_copy_buffer_to_image2(cmd, ©);
|
||||
}
|
||||
false => {
|
||||
let copy = CopyImageToBufferInfo2::default()
|
||||
.src_image(img.img)
|
||||
.src_image_layout(image_layout)
|
||||
.dst_buffer(buf.buf)
|
||||
.regions(®ions);
|
||||
dev.cmd_copy_image_to_buffer2(cmd, ©);
|
||||
}
|
||||
}
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
CopyDeviceCopyType::ImageToImage { src, dst } => {
|
||||
let regions = &mut *slf.dev.phy.image_copy_2.borrow_mut();
|
||||
regions.clear();
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let region = ImageCopy2::default()
|
||||
.src_subresource(image_subresource)
|
||||
.src_offset(Offset3D { x, y, z: 0 })
|
||||
.dst_subresource(image_subresource)
|
||||
.dst_offset(Offset3D { x, y, z: 0 })
|
||||
.extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
});
|
||||
regions.push(region);
|
||||
}
|
||||
use {AccessFlags2 as A, ImageLayout as L};
|
||||
let initial_barriers = initial_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let final_barriers = final_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let initial_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&initial_barriers);
|
||||
let final_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&final_barriers);
|
||||
let copy_image_info = CopyImageInfo2::default()
|
||||
.src_image(src.img)
|
||||
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
|
||||
.dst_image(dst.img)
|
||||
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
|
||||
.regions(regions);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
dev.cmd_copy_image2(cmd, ©_image_info);
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
CopyDeviceCopyType::Blit { src, dst } => {
|
||||
let regions = &mut *slf.dev.phy.image_blit_2.borrow_mut();
|
||||
regions.clear();
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let x1 = x;
|
||||
let y1 = y;
|
||||
let x2 = x1 + width as i32;
|
||||
let y2 = y1 + height as i32;
|
||||
let offsets = [
|
||||
Offset3D { x: x1, y: y1, z: 0 },
|
||||
Offset3D { x: x2, y: y2, z: 1 },
|
||||
];
|
||||
let region = ImageBlit2::default()
|
||||
.src_subresource(image_subresource)
|
||||
.src_offsets(offsets)
|
||||
.dst_subresource(image_subresource)
|
||||
.dst_offsets(offsets);
|
||||
regions.push(region);
|
||||
}
|
||||
use {AccessFlags2 as A, ImageLayout as L};
|
||||
let initial_barriers = initial_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let final_barriers = final_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let initial_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&initial_barriers);
|
||||
let final_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&final_barriers);
|
||||
let blit_image_info = BlitImageInfo2::default()
|
||||
.src_image(src.img)
|
||||
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
|
||||
.dst_image(dst.img)
|
||||
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
|
||||
.regions(regions)
|
||||
.filter(Filter::NEAREST);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
dev.cmd_blit_image2(cmd, &blit_image_info);
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
dev.end_command_buffer(cmd)
|
||||
.map_err(CopyDeviceError::EndCommandBuffer)?;
|
||||
}
|
||||
let mut wait_semaphore = None;
|
||||
let mut wait_semaphores = ArrayVec::<_, 1>::new();
|
||||
if let Some(sync) = sync
|
||||
&& let Some(sync_file) = sync.get_sync_file()
|
||||
{
|
||||
let semaphore = match slf.dev.semaphores.pop() {
|
||||
Some(s) => s,
|
||||
_ => slf.dev.create_semaphore()?,
|
||||
};
|
||||
semaphore.import(sync_file)?;
|
||||
let info = SemaphoreSubmitInfo::default()
|
||||
.semaphore(semaphore.semaphore)
|
||||
.stage_mask(PipelineStageFlags2::TRANSFER);
|
||||
wait_semaphores.push(info);
|
||||
wait_semaphore = Some(semaphore);
|
||||
}
|
||||
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd);
|
||||
let mut semaphore_submit_info = SemaphoreSubmitInfo::default();
|
||||
let mut submit_info = SubmitInfo2::default()
|
||||
.command_buffer_infos(slice::from_ref(&command_buffer_info))
|
||||
.wait_semaphore_infos(&wait_semaphores);
|
||||
let vulkan_sync = slf.dev.create_sync(
|
||||
self.dev.timeline_semaphore.as_ref(),
|
||||
&mut semaphore_submit_info,
|
||||
&mut submit_info,
|
||||
)?;
|
||||
unsafe {
|
||||
slf.dev
|
||||
.dev
|
||||
.queue_submit2(
|
||||
slf.dev.queues[tt],
|
||||
slice::from_ref(&submit_info),
|
||||
vulkan_sync.fence(),
|
||||
)
|
||||
.map_err(CopyDeviceError::SubmitCopy)?;
|
||||
}
|
||||
let sync = vulkan_sync.to_sync(|| slf.dev.wait_idle());
|
||||
slf.busy.set(sync.clone());
|
||||
let pending = Pending {
|
||||
dev: slf.dev.clone(),
|
||||
busy_id: slf.busy_id.add_fetch(1),
|
||||
sync: sync.clone(),
|
||||
copy: self.inner.clone(),
|
||||
semaphore: wait_semaphore,
|
||||
vulkan_sync,
|
||||
};
|
||||
slf.dev.submissions[tt].pending.push(pending);
|
||||
Ok(sync)
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanSemaphore {
|
||||
fn import(&self, sync_file: &OwnedFd) -> Result<(), CopyDeviceError> {
|
||||
let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0)
|
||||
|
|
@ -1733,47 +1288,6 @@ impl VulkanSemaphore {
|
|||
}
|
||||
}
|
||||
|
||||
impl CopyDeviceRegistry {
|
||||
pub fn new(
|
||||
ring: &Rc<IoUring>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
eventfd_cache: &Rc<EventfdCache>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ring: ring.clone(),
|
||||
eng: eng.clone(),
|
||||
eventfd_cache: eventfd_cache.clone(),
|
||||
devs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&self, dev: c::dev_t) {
|
||||
self.devs.remove(&dev);
|
||||
}
|
||||
|
||||
pub fn get(&self, dev: c::dev_t) -> Option<Rc<PhysicalCopyDevice>> {
|
||||
if let Some(dev) = self.devs.get(&dev) {
|
||||
return dev;
|
||||
}
|
||||
match PhysicalCopyDevice::new(&self.ring, &self.eng, &self.eventfd_cache, dev).map(Some) {
|
||||
Ok(cd) => {
|
||||
self.devs.set(dev, cd.clone());
|
||||
cd
|
||||
}
|
||||
Err(e) => {
|
||||
let maj = uapi::major(dev);
|
||||
let min = uapi::minor(dev);
|
||||
log::warn!(
|
||||
"Could not create physical copy device for {maj}:{min}: {}",
|
||||
ErrorFmt(e),
|
||||
);
|
||||
self.devs.set(dev, None);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanSemaphore {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
|
@ -1898,125 +1412,6 @@ impl Deref for CopyDevice {
|
|||
}
|
||||
}
|
||||
|
||||
type QueueInfo = (u32, (u32, u32), u32);
|
||||
|
||||
fn allocate_queues(
|
||||
gfx: QueueInfo,
|
||||
compute_only: Option<QueueInfo>,
|
||||
transfer_only: Option<QueueInfo>,
|
||||
) -> (Vec<QueueToAllocate>, KeyedCopy<QueueIndex>) {
|
||||
let intra = compute_only.unwrap_or(gfx);
|
||||
let cross = transfer_only.unwrap_or(intra);
|
||||
let mut distinct_families = AHashSet::default();
|
||||
distinct_families.insert(cross);
|
||||
distinct_families.insert(intra);
|
||||
distinct_families.insert(gfx);
|
||||
let mut queues_to_allocate = vec![];
|
||||
macro_rules! index {
|
||||
($qi:expr, $within:expr) => {
|
||||
QueueIndex {
|
||||
allocate_idx: queues_to_allocate.len(),
|
||||
family: $qi.0,
|
||||
idx_within_family: $within as u32,
|
||||
transfer_granularity_mask: $qi.1,
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! alloc {
|
||||
($qi:expr, $num:expr) => {
|
||||
QueueToAllocate {
|
||||
family: $qi.0,
|
||||
num: $num as usize,
|
||||
}
|
||||
};
|
||||
}
|
||||
let (blit, intra_idx, download, upload);
|
||||
if distinct_families.len() == 3 {
|
||||
let num_cross = cross.2.min(2) as usize;
|
||||
blit = index!(gfx, 0);
|
||||
queues_to_allocate.push(alloc!(gfx, 1));
|
||||
intra_idx = index!(intra, 0);
|
||||
queues_to_allocate.push(alloc!(intra, 1));
|
||||
download = index!(cross, 0);
|
||||
upload = index!(cross, num_cross - 1);
|
||||
queues_to_allocate.push(alloc!(cross, num_cross));
|
||||
} else if distinct_families.len() == 1 {
|
||||
let qi = cross;
|
||||
let num = qi.2.min(4);
|
||||
match num {
|
||||
1 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 0);
|
||||
download = index!(qi, 0);
|
||||
upload = index!(qi, 0);
|
||||
}
|
||||
2 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 0);
|
||||
download = index!(qi, 0);
|
||||
upload = index!(qi, 1);
|
||||
}
|
||||
3 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 0);
|
||||
download = index!(qi, 1);
|
||||
upload = index!(qi, 2);
|
||||
}
|
||||
4 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 1);
|
||||
download = index!(qi, 2);
|
||||
upload = index!(qi, 3);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
queues_to_allocate.push(alloc!(qi, num));
|
||||
} else {
|
||||
if gfx == intra {
|
||||
let num_gfx = gfx.2.min(2);
|
||||
blit = index!(gfx, 0);
|
||||
intra_idx = index!(gfx, num_gfx - 1);
|
||||
queues_to_allocate.push(alloc!(gfx, num_gfx));
|
||||
let num_cross = cross.2.min(2);
|
||||
download = index!(cross, 0);
|
||||
upload = index!(cross, num_cross - 1);
|
||||
queues_to_allocate.push(alloc!(cross, num_cross));
|
||||
} else {
|
||||
// if cross == gfx then intra == gfx
|
||||
assert_eq!(intra, cross);
|
||||
blit = index!(gfx, 0);
|
||||
queues_to_allocate.push(alloc!(gfx, 1));
|
||||
let num_intra = intra.2.min(3);
|
||||
match num_intra {
|
||||
1 => {
|
||||
intra_idx = index!(intra, 0);
|
||||
download = index!(intra, 0);
|
||||
upload = index!(intra, 0);
|
||||
}
|
||||
2 => {
|
||||
intra_idx = index!(intra, 0);
|
||||
download = index!(intra, 0);
|
||||
upload = index!(intra, 1);
|
||||
}
|
||||
3 => {
|
||||
intra_idx = index!(intra, 0);
|
||||
download = index!(intra, 1);
|
||||
upload = index!(intra, 2);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
queues_to_allocate.push(alloc!(intra, num_intra));
|
||||
}
|
||||
}
|
||||
let queue_indices = static_copy_map! {
|
||||
TransferType::Blit => blit,
|
||||
TransferType::Intra => intra_idx,
|
||||
TransferType::Download => download,
|
||||
TransferType::Upload => upload,
|
||||
};
|
||||
(queues_to_allocate, queue_indices)
|
||||
}
|
||||
|
||||
impl VulkanDeviceInf for CopyDeviceInner {
|
||||
fn instance(&self) -> &VulkanCoreInstance {
|
||||
&self.phy.instance
|
||||
|
|
|
|||
442
src/copy_device/execute.rs
Normal file
442
src/copy_device/execute.rs
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
use {
|
||||
super::{CopyDeviceCopy, CopyDeviceCopyType, CopyDeviceError, Pending},
|
||||
crate::{
|
||||
gfx_api::FdSync,
|
||||
rect::{Rect, Region},
|
||||
vulkan_core::sync::VulkanDeviceSyncExt,
|
||||
},
|
||||
arrayvec::ArrayVec,
|
||||
ash::vk::{
|
||||
AccessFlags2, BlitImageInfo2, BufferCopy2, BufferImageCopy2, BufferMemoryBarrier2,
|
||||
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
|
||||
CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2, CopyImageToBufferInfo2,
|
||||
DependencyInfo, Extent3D, Filter, ImageAspectFlags, ImageBlit2, ImageCopy2, ImageLayout,
|
||||
ImageMemoryBarrier2, ImageSubresourceLayers, ImageSubresourceRange, Offset3D,
|
||||
PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, SemaphoreSubmitInfo, SubmitInfo2,
|
||||
WHOLE_SIZE,
|
||||
},
|
||||
std::slice,
|
||||
};
|
||||
|
||||
impl CopyDeviceCopy {
|
||||
fn ensure_not_busy(&self) -> Result<(), CopyDeviceError> {
|
||||
let slf = &*self.inner;
|
||||
if let Some(sync) = slf.busy.get()
|
||||
&& sync.is_unsignaled()
|
||||
{
|
||||
return Err(CopyDeviceError::Busy);
|
||||
}
|
||||
slf.busy.take();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn execute(
|
||||
&self,
|
||||
sync: Option<&FdSync>,
|
||||
region: Option<&Region>,
|
||||
) -> Result<Option<FdSync>, CopyDeviceError> {
|
||||
self.ensure_not_busy()?;
|
||||
let slf = &*self.inner;
|
||||
let tt = slf.tt;
|
||||
let dev = &slf.dev.dev;
|
||||
let cmd = slf.command_buffer;
|
||||
let queue_family = slf.dev.phy.queues[tt].family;
|
||||
let region_buf;
|
||||
let width = slf.width;
|
||||
let height = slf.height;
|
||||
let region = match region {
|
||||
Some(r) => r,
|
||||
_ => {
|
||||
region_buf = Region::new(Rect::new_saturating(0, 0, width as i32, height as i32));
|
||||
®ion_buf
|
||||
}
|
||||
};
|
||||
let (x_mask, y_mask) = slf.dev.phy.queues[tt].transfer_granularity_mask;
|
||||
let rects = &mut *slf.dev.phy.rects.borrow_mut();
|
||||
rects.clear();
|
||||
for rect in region.iter() {
|
||||
let x1 = (rect.x1().max(0) as u32 & !x_mask).min(width);
|
||||
let y1 = (rect.y1().max(0) as u32 & !y_mask).min(height);
|
||||
let x2 = ((rect.x2().max(0) as u32 + x_mask) & !x_mask).min(width);
|
||||
let y2 = ((rect.y2().max(0) as u32 + y_mask) & !y_mask).min(height);
|
||||
let width = x2 - x1;
|
||||
let height = y2 - y1;
|
||||
if width == 0 || height == 0 {
|
||||
continue;
|
||||
}
|
||||
rects.push((x1 as i32, y1 as i32, width, height));
|
||||
}
|
||||
if rects.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let begin_info =
|
||||
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||
unsafe {
|
||||
dev.begin_command_buffer(cmd, &begin_info)
|
||||
.map_err(CopyDeviceError::BeginCommandBuffer)?;
|
||||
}
|
||||
macro_rules! initial_buffer_barriers {
|
||||
($($buf:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
BufferMemoryBarrier2::default()
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.buffer($buf.buf)
|
||||
.size(WHOLE_SIZE),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
macro_rules! final_buffer_barriers {
|
||||
($($buf:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
BufferMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.buffer($buf.buf)
|
||||
.size(WHOLE_SIZE),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
let image_subresource_range = ImageSubresourceRange {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
};
|
||||
let image_subresource = ImageSubresourceLayers {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
mip_level: 0,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
};
|
||||
macro_rules! initial_image_barriers {
|
||||
($($img:expr, $layout:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
ImageMemoryBarrier2::default()
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.old_layout(ImageLayout::GENERAL)
|
||||
.new_layout(ImageLayout::GENERAL)
|
||||
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.old_layout(ImageLayout::GENERAL)
|
||||
.new_layout($layout)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
macro_rules! final_image_barriers {
|
||||
($($img:expr, $layout:expr, $access:expr;)*) => {
|
||||
[$(
|
||||
ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask($access)
|
||||
.old_layout($layout)
|
||||
.new_layout(ImageLayout::GENERAL)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(queue_family)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.src_access_mask($access)
|
||||
.old_layout(ImageLayout::GENERAL)
|
||||
.new_layout(ImageLayout::GENERAL)
|
||||
.src_queue_family_index(queue_family)
|
||||
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
.image($img.img)
|
||||
.subresource_range(image_subresource_range),
|
||||
)*]
|
||||
};
|
||||
}
|
||||
match &slf.ty {
|
||||
CopyDeviceCopyType::BufferToBuffer {
|
||||
src,
|
||||
dst,
|
||||
stride,
|
||||
bpp,
|
||||
} => {
|
||||
let regions = &mut *slf.dev.phy.buffer_copy_2.borrow_mut();
|
||||
regions.clear();
|
||||
let stride = *stride as u64;
|
||||
let bpp = *bpp as u64;
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let lo = y as u64 * stride + x as u64 * bpp;
|
||||
let size = (height as u64 - 1) * stride + width as u64 * bpp;
|
||||
let region = BufferCopy2::default()
|
||||
.src_offset(lo)
|
||||
.dst_offset(lo)
|
||||
.size(size);
|
||||
regions.push(region);
|
||||
}
|
||||
use AccessFlags2 as A;
|
||||
let initial_barriers = initial_buffer_barriers![
|
||||
src, A::TRANSFER_READ;
|
||||
dst, A::TRANSFER_WRITE;
|
||||
];
|
||||
let final_barriers = final_buffer_barriers![
|
||||
src, A::TRANSFER_READ;
|
||||
dst, A::TRANSFER_WRITE;
|
||||
];
|
||||
let initial_dependency_info =
|
||||
DependencyInfo::default().buffer_memory_barriers(&initial_barriers);
|
||||
let final_dependency_info =
|
||||
DependencyInfo::default().buffer_memory_barriers(&final_barriers);
|
||||
let copy_buffer_info = CopyBufferInfo2::default()
|
||||
.src_buffer(src.buf)
|
||||
.dst_buffer(dst.buf)
|
||||
.regions(regions);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
dev.cmd_copy_buffer2(cmd, ©_buffer_info);
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
CopyDeviceCopyType::BufferToImage {
|
||||
buf,
|
||||
buf_format,
|
||||
buf_stride,
|
||||
img,
|
||||
}
|
||||
| CopyDeviceCopyType::ImageToBuffer {
|
||||
img,
|
||||
buf,
|
||||
buf_format,
|
||||
buf_stride,
|
||||
} => {
|
||||
let regions = &mut *slf.dev.phy.buffer_image_copy_2.borrow_mut();
|
||||
regions.clear();
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let offset = y as u64 * *buf_stride as u64 + x as u64 * buf_format.bpp as u64;
|
||||
let region = BufferImageCopy2::default()
|
||||
.buffer_offset(offset)
|
||||
.buffer_row_length(*buf_stride / buf_format.bpp)
|
||||
.buffer_image_height(slf.height)
|
||||
.image_subresource(image_subresource)
|
||||
.image_offset(Offset3D { x, y, z: 0 })
|
||||
.image_extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
});
|
||||
regions.push(region);
|
||||
}
|
||||
let buffer_to_image = match &slf.ty {
|
||||
CopyDeviceCopyType::BufferToImage { .. } => true,
|
||||
CopyDeviceCopyType::ImageToBuffer { .. } => false,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let image_access_mask;
|
||||
let image_layout;
|
||||
let buffer_access_mask;
|
||||
match buffer_to_image {
|
||||
true => {
|
||||
image_access_mask = AccessFlags2::TRANSFER_WRITE;
|
||||
image_layout = ImageLayout::TRANSFER_DST_OPTIMAL;
|
||||
buffer_access_mask = AccessFlags2::TRANSFER_READ;
|
||||
}
|
||||
false => {
|
||||
image_access_mask = AccessFlags2::TRANSFER_READ;
|
||||
image_layout = ImageLayout::TRANSFER_SRC_OPTIMAL;
|
||||
buffer_access_mask = AccessFlags2::TRANSFER_WRITE;
|
||||
}
|
||||
}
|
||||
let initial_image_barriers = initial_image_barriers![
|
||||
img, image_layout, image_access_mask;
|
||||
];
|
||||
let final_image_barriers = final_image_barriers![
|
||||
img, image_layout, image_access_mask;
|
||||
];
|
||||
let initial_buffer_barriers = initial_buffer_barriers![
|
||||
buf, buffer_access_mask;
|
||||
];
|
||||
let final_buffer_barriers = final_buffer_barriers![
|
||||
buf, buffer_access_mask;
|
||||
];
|
||||
let initial_dependency_info = DependencyInfo::default()
|
||||
.buffer_memory_barriers(&initial_buffer_barriers)
|
||||
.image_memory_barriers(&initial_image_barriers);
|
||||
let final_dependency_info = DependencyInfo::default()
|
||||
.buffer_memory_barriers(&final_buffer_barriers)
|
||||
.image_memory_barriers(&final_image_barriers);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
match buffer_to_image {
|
||||
true => {
|
||||
let copy = CopyBufferToImageInfo2::default()
|
||||
.src_buffer(buf.buf)
|
||||
.dst_image(img.img)
|
||||
.dst_image_layout(image_layout)
|
||||
.regions(®ions);
|
||||
dev.cmd_copy_buffer_to_image2(cmd, ©);
|
||||
}
|
||||
false => {
|
||||
let copy = CopyImageToBufferInfo2::default()
|
||||
.src_image(img.img)
|
||||
.src_image_layout(image_layout)
|
||||
.dst_buffer(buf.buf)
|
||||
.regions(®ions);
|
||||
dev.cmd_copy_image_to_buffer2(cmd, ©);
|
||||
}
|
||||
}
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
CopyDeviceCopyType::ImageToImage { src, dst } => {
|
||||
let regions = &mut *slf.dev.phy.image_copy_2.borrow_mut();
|
||||
regions.clear();
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let region = ImageCopy2::default()
|
||||
.src_subresource(image_subresource)
|
||||
.src_offset(Offset3D { x, y, z: 0 })
|
||||
.dst_subresource(image_subresource)
|
||||
.dst_offset(Offset3D { x, y, z: 0 })
|
||||
.extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
});
|
||||
regions.push(region);
|
||||
}
|
||||
use {AccessFlags2 as A, ImageLayout as L};
|
||||
let initial_barriers = initial_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let final_barriers = final_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let initial_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&initial_barriers);
|
||||
let final_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&final_barriers);
|
||||
let copy_image_info = CopyImageInfo2::default()
|
||||
.src_image(src.img)
|
||||
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
|
||||
.dst_image(dst.img)
|
||||
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
|
||||
.regions(regions);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
dev.cmd_copy_image2(cmd, ©_image_info);
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
CopyDeviceCopyType::Blit { src, dst } => {
|
||||
let regions = &mut *slf.dev.phy.image_blit_2.borrow_mut();
|
||||
regions.clear();
|
||||
for &mut (x, y, width, height) in rects {
|
||||
let x1 = x;
|
||||
let y1 = y;
|
||||
let x2 = x1 + width as i32;
|
||||
let y2 = y1 + height as i32;
|
||||
let offsets = [
|
||||
Offset3D { x: x1, y: y1, z: 0 },
|
||||
Offset3D { x: x2, y: y2, z: 1 },
|
||||
];
|
||||
let region = ImageBlit2::default()
|
||||
.src_subresource(image_subresource)
|
||||
.src_offsets(offsets)
|
||||
.dst_subresource(image_subresource)
|
||||
.dst_offsets(offsets);
|
||||
regions.push(region);
|
||||
}
|
||||
use {AccessFlags2 as A, ImageLayout as L};
|
||||
let initial_barriers = initial_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let final_barriers = final_image_barriers![
|
||||
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
|
||||
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
|
||||
];
|
||||
let initial_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&initial_barriers);
|
||||
let final_dependency_info =
|
||||
DependencyInfo::default().image_memory_barriers(&final_barriers);
|
||||
let blit_image_info = BlitImageInfo2::default()
|
||||
.src_image(src.img)
|
||||
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
|
||||
.dst_image(dst.img)
|
||||
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
|
||||
.regions(regions)
|
||||
.filter(Filter::NEAREST);
|
||||
unsafe {
|
||||
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
|
||||
dev.cmd_blit_image2(cmd, &blit_image_info);
|
||||
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
|
||||
}
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
dev.end_command_buffer(cmd)
|
||||
.map_err(CopyDeviceError::EndCommandBuffer)?;
|
||||
}
|
||||
let mut wait_semaphore = None;
|
||||
let mut wait_semaphores = ArrayVec::<_, 1>::new();
|
||||
if let Some(sync) = sync
|
||||
&& let Some(sync_file) = sync.get_sync_file()
|
||||
{
|
||||
let semaphore = match slf.dev.semaphores.pop() {
|
||||
Some(s) => s,
|
||||
_ => slf.dev.create_semaphore()?,
|
||||
};
|
||||
semaphore.import(sync_file)?;
|
||||
let info = SemaphoreSubmitInfo::default()
|
||||
.semaphore(semaphore.semaphore)
|
||||
.stage_mask(PipelineStageFlags2::TRANSFER);
|
||||
wait_semaphores.push(info);
|
||||
wait_semaphore = Some(semaphore);
|
||||
}
|
||||
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd);
|
||||
let mut semaphore_submit_info = SemaphoreSubmitInfo::default();
|
||||
let mut submit_info = SubmitInfo2::default()
|
||||
.command_buffer_infos(slice::from_ref(&command_buffer_info))
|
||||
.wait_semaphore_infos(&wait_semaphores);
|
||||
let vulkan_sync = slf.dev.create_sync(
|
||||
self.dev.timeline_semaphore.as_ref(),
|
||||
&mut semaphore_submit_info,
|
||||
&mut submit_info,
|
||||
)?;
|
||||
unsafe {
|
||||
slf.dev
|
||||
.dev
|
||||
.queue_submit2(
|
||||
slf.dev.queues[tt],
|
||||
slice::from_ref(&submit_info),
|
||||
vulkan_sync.fence(),
|
||||
)
|
||||
.map_err(CopyDeviceError::SubmitCopy)?;
|
||||
}
|
||||
let sync = vulkan_sync.to_sync(|| slf.dev.wait_idle());
|
||||
slf.busy.set(sync.clone());
|
||||
let pending = Pending {
|
||||
dev: slf.dev.clone(),
|
||||
busy_id: slf.busy_id.add_fetch(1),
|
||||
sync: sync.clone(),
|
||||
copy: self.inner.clone(),
|
||||
semaphore: wait_semaphore,
|
||||
vulkan_sync,
|
||||
};
|
||||
slf.dev.submissions[tt].pending.push(pending);
|
||||
Ok(sync)
|
||||
}
|
||||
}
|
||||
138
src/copy_device/queue_allocation.rs
Normal file
138
src/copy_device/queue_allocation.rs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
use {
|
||||
super::{KeyedCopy, TransferType},
|
||||
ahash::AHashSet,
|
||||
linearize::static_copy_map,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct QueueToAllocate {
|
||||
pub(super) family: u32,
|
||||
pub(super) num: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub(super) struct QueueIndex {
|
||||
pub(super) allocate_idx: usize,
|
||||
pub(super) family: u32,
|
||||
pub(super) idx_within_family: u32,
|
||||
pub(super) transfer_granularity_mask: (u32, u32),
|
||||
}
|
||||
|
||||
type QueueInfo = (u32, (u32, u32), u32);
|
||||
|
||||
pub(super) fn allocate_queues(
|
||||
gfx: QueueInfo,
|
||||
compute_only: Option<QueueInfo>,
|
||||
transfer_only: Option<QueueInfo>,
|
||||
) -> (Vec<QueueToAllocate>, KeyedCopy<QueueIndex>) {
|
||||
let intra = compute_only.unwrap_or(gfx);
|
||||
let cross = transfer_only.unwrap_or(intra);
|
||||
let mut distinct_families = AHashSet::default();
|
||||
distinct_families.insert(cross);
|
||||
distinct_families.insert(intra);
|
||||
distinct_families.insert(gfx);
|
||||
let mut queues_to_allocate = vec![];
|
||||
macro_rules! index {
|
||||
($qi:expr, $within:expr) => {
|
||||
QueueIndex {
|
||||
allocate_idx: queues_to_allocate.len(),
|
||||
family: $qi.0,
|
||||
idx_within_family: $within as u32,
|
||||
transfer_granularity_mask: $qi.1,
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! alloc {
|
||||
($qi:expr, $num:expr) => {
|
||||
QueueToAllocate {
|
||||
family: $qi.0,
|
||||
num: $num as usize,
|
||||
}
|
||||
};
|
||||
}
|
||||
let (blit, intra_idx, download, upload);
|
||||
if distinct_families.len() == 3 {
|
||||
let num_cross = cross.2.min(2) as usize;
|
||||
blit = index!(gfx, 0);
|
||||
queues_to_allocate.push(alloc!(gfx, 1));
|
||||
intra_idx = index!(intra, 0);
|
||||
queues_to_allocate.push(alloc!(intra, 1));
|
||||
download = index!(cross, 0);
|
||||
upload = index!(cross, num_cross - 1);
|
||||
queues_to_allocate.push(alloc!(cross, num_cross));
|
||||
} else if distinct_families.len() == 1 {
|
||||
let qi = cross;
|
||||
let num = qi.2.min(4);
|
||||
match num {
|
||||
1 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 0);
|
||||
download = index!(qi, 0);
|
||||
upload = index!(qi, 0);
|
||||
}
|
||||
2 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 0);
|
||||
download = index!(qi, 0);
|
||||
upload = index!(qi, 1);
|
||||
}
|
||||
3 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 0);
|
||||
download = index!(qi, 1);
|
||||
upload = index!(qi, 2);
|
||||
}
|
||||
4 => {
|
||||
blit = index!(qi, 0);
|
||||
intra_idx = index!(qi, 1);
|
||||
download = index!(qi, 2);
|
||||
upload = index!(qi, 3);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
queues_to_allocate.push(alloc!(qi, num));
|
||||
} else {
|
||||
if gfx == intra {
|
||||
let num_gfx = gfx.2.min(2);
|
||||
blit = index!(gfx, 0);
|
||||
intra_idx = index!(gfx, num_gfx - 1);
|
||||
queues_to_allocate.push(alloc!(gfx, num_gfx));
|
||||
let num_cross = cross.2.min(2);
|
||||
download = index!(cross, 0);
|
||||
upload = index!(cross, num_cross - 1);
|
||||
queues_to_allocate.push(alloc!(cross, num_cross));
|
||||
} else {
|
||||
// if cross == gfx then intra == gfx
|
||||
assert_eq!(intra, cross);
|
||||
blit = index!(gfx, 0);
|
||||
queues_to_allocate.push(alloc!(gfx, 1));
|
||||
let num_intra = intra.2.min(3);
|
||||
match num_intra {
|
||||
1 => {
|
||||
intra_idx = index!(intra, 0);
|
||||
download = index!(intra, 0);
|
||||
upload = index!(intra, 0);
|
||||
}
|
||||
2 => {
|
||||
intra_idx = index!(intra, 0);
|
||||
download = index!(intra, 0);
|
||||
upload = index!(intra, 1);
|
||||
}
|
||||
3 => {
|
||||
intra_idx = index!(intra, 0);
|
||||
download = index!(intra, 1);
|
||||
upload = index!(intra, 2);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
queues_to_allocate.push(alloc!(intra, num_intra));
|
||||
}
|
||||
}
|
||||
let queue_indices = static_copy_map! {
|
||||
TransferType::Blit => blit,
|
||||
TransferType::Intra => intra_idx,
|
||||
TransferType::Download => download,
|
||||
TransferType::Upload => upload,
|
||||
};
|
||||
(queues_to_allocate, queue_indices)
|
||||
}
|
||||
59
src/copy_device/registry.rs
Normal file
59
src/copy_device/registry.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use {
|
||||
super::PhysicalCopyDevice,
|
||||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
eventfd_cache::EventfdCache,
|
||||
io_uring::IoUring,
|
||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt},
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub struct CopyDeviceRegistry {
|
||||
ring: Rc<IoUring>,
|
||||
eng: Rc<AsyncEngine>,
|
||||
eventfd_cache: Rc<EventfdCache>,
|
||||
devs: CopyHashMap<c::dev_t, Option<Rc<PhysicalCopyDevice>>>,
|
||||
}
|
||||
|
||||
impl CopyDeviceRegistry {
|
||||
pub fn new(
|
||||
ring: &Rc<IoUring>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
eventfd_cache: &Rc<EventfdCache>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ring: ring.clone(),
|
||||
eng: eng.clone(),
|
||||
eventfd_cache: eventfd_cache.clone(),
|
||||
devs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&self, dev: c::dev_t) {
|
||||
self.devs.remove(&dev);
|
||||
}
|
||||
|
||||
pub fn get(&self, dev: c::dev_t) -> Option<Rc<PhysicalCopyDevice>> {
|
||||
if let Some(dev) = self.devs.get(&dev) {
|
||||
return dev;
|
||||
}
|
||||
match PhysicalCopyDevice::new(&self.ring, &self.eng, &self.eventfd_cache, dev).map(Some) {
|
||||
Ok(cd) => {
|
||||
self.devs.set(dev, cd.clone());
|
||||
cd
|
||||
}
|
||||
Err(e) => {
|
||||
let maj = uapi::major(dev);
|
||||
let min = uapi::minor(dev);
|
||||
log::warn!(
|
||||
"Could not create physical copy device for {maj}:{min}: {}",
|
||||
ErrorFmt(e),
|
||||
);
|
||||
self.devs.set(dev, None);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,490 +1 @@
|
|||
pub mod jobs;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
io_uring::IoUring,
|
||||
utils::{
|
||||
buf::TypedBuf,
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
oserror::{OsError, OsErrorExt2},
|
||||
pipe::{Pipe, pipe},
|
||||
ptr_ext::MutPtrExt,
|
||||
queue::AsyncQueue,
|
||||
stack::Stack,
|
||||
},
|
||||
},
|
||||
parking_lot::{Condvar, Mutex},
|
||||
std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
collections::VecDeque,
|
||||
mem,
|
||||
ptr::NonNull,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
thread,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{OwnedFd, c},
|
||||
};
|
||||
|
||||
pub trait CpuJob {
|
||||
fn work(&mut self) -> &mut dyn CpuWork;
|
||||
fn completed(self: Box<Self>);
|
||||
}
|
||||
|
||||
pub trait CpuWork: Send {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>>;
|
||||
|
||||
fn cancel_async(&mut self, ring: &Rc<IoUring>) {
|
||||
let _ = ring;
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||
let _ = work;
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsyncCpuWork: Any {
|
||||
fn run(
|
||||
self: Box<Self>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
completion: WorkCompletion,
|
||||
) -> SpawnedFuture<CompletedWork>;
|
||||
}
|
||||
|
||||
pub struct WorkCompletion {
|
||||
worker: Rc<Worker>,
|
||||
id: CpuJobId,
|
||||
}
|
||||
|
||||
pub struct CompletedWork(());
|
||||
|
||||
impl WorkCompletion {
|
||||
pub fn complete(self, work: Box<dyn AsyncCpuWork>) -> CompletedWork {
|
||||
let job = self.worker.async_jobs.remove(&self.id).unwrap();
|
||||
unsafe {
|
||||
job.work.deref_mut().async_work_done(work);
|
||||
}
|
||||
self.worker.send_completion(self.id);
|
||||
CompletedWork(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CpuWorker {
|
||||
data: Rc<CpuWorkerData>,
|
||||
_completions_listener: SpawnedFuture<()>,
|
||||
_job_enqueuer: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct PendingJob {
|
||||
id: CpuJobId,
|
||||
thread_data: Rc<CpuWorkerData>,
|
||||
job_data: Rc<PendingJobData>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||
enum PendingJobState {
|
||||
#[default]
|
||||
Waiting,
|
||||
Abandoned,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PendingJobData {
|
||||
job: Cell<Option<NonNull<dyn CpuJob>>>,
|
||||
state: Cell<PendingJobState>,
|
||||
}
|
||||
|
||||
enum Job {
|
||||
New {
|
||||
id: CpuJobId,
|
||||
work: *mut dyn CpuWork,
|
||||
},
|
||||
Cancel {
|
||||
id: CpuJobId,
|
||||
},
|
||||
}
|
||||
|
||||
unsafe impl Send for Job {}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CompletedJobsExchange {
|
||||
queue: VecDeque<CpuJobId>,
|
||||
condvar: Option<Arc<Condvar>>,
|
||||
}
|
||||
|
||||
struct CpuWorkerData {
|
||||
next: CpuJobIds,
|
||||
jobs_to_enqueue: AsyncQueue<Job>,
|
||||
new_jobs: Arc<Mutex<VecDeque<Job>>>,
|
||||
have_new_jobs: Rc<OwnedFd>,
|
||||
completed_jobs_remote: Arc<Mutex<CompletedJobsExchange>>,
|
||||
completed_jobs_local: RefCell<VecDeque<CpuJobId>>,
|
||||
have_completed_jobs: Rc<OwnedFd>,
|
||||
pending_jobs: CopyHashMap<CpuJobId, Rc<PendingJobData>>,
|
||||
ring: Rc<IoUring>,
|
||||
_stop: OwnedFd,
|
||||
pending_job_data_cache: Stack<Rc<PendingJobData>>,
|
||||
sync_wake_condvar: Arc<Condvar>,
|
||||
}
|
||||
|
||||
linear_ids!(CpuJobIds, CpuJobId, u64);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CpuWorkerError {
|
||||
#[error("Could not create a pipe")]
|
||||
Pipe(#[source] OsError),
|
||||
#[error("Could not create an eventfd")]
|
||||
EventFd(#[source] OsError),
|
||||
#[error("Could not dup an eventfd")]
|
||||
Dup(#[source] OsError),
|
||||
}
|
||||
|
||||
impl PendingJob {
|
||||
pub fn detach(self) {
|
||||
match self.job_data.state.get() {
|
||||
PendingJobState::Waiting => {
|
||||
self.job_data.state.set(PendingJobState::Abandoned);
|
||||
}
|
||||
PendingJobState::Abandoned => {
|
||||
unreachable!();
|
||||
}
|
||||
PendingJobState::Completed => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CpuWorker {
|
||||
fn drop(&mut self) {
|
||||
self.data.do_equeue_jobs();
|
||||
if self.data.pending_jobs.is_not_empty() {
|
||||
log::warn!("CpuWorker dropped with pending jobs. Completed jobs will not be triggered.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PendingJob {
|
||||
fn drop(&mut self) {
|
||||
match self.job_data.state.get() {
|
||||
PendingJobState::Waiting => {
|
||||
log::warn!("PendingJob dropped before completion. Blocking.");
|
||||
let data = &self.thread_data;
|
||||
let id = self.id;
|
||||
self.job_data.state.set(PendingJobState::Abandoned);
|
||||
data.jobs_to_enqueue.push(Job::Cancel { id });
|
||||
data.do_equeue_jobs();
|
||||
loop {
|
||||
data.dispatch_completions();
|
||||
if !data.pending_jobs.contains(&id) {
|
||||
break;
|
||||
}
|
||||
let mut remote = data.completed_jobs_remote.lock();
|
||||
while remote.queue.is_empty() {
|
||||
remote.condvar = Some(data.sync_wake_condvar.clone());
|
||||
data.sync_wake_condvar.wait(&mut remote);
|
||||
}
|
||||
}
|
||||
}
|
||||
PendingJobState::Abandoned => {}
|
||||
PendingJobState::Completed => {
|
||||
self.thread_data
|
||||
.pending_job_data_cache
|
||||
.push(self.job_data.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWorkerData {
|
||||
fn clear(&self) {
|
||||
self.jobs_to_enqueue.clear();
|
||||
self.new_jobs.lock().clear();
|
||||
self.completed_jobs_remote.lock().queue.clear();
|
||||
self.completed_jobs_local.borrow_mut().clear();
|
||||
self.pending_jobs.clear();
|
||||
self.pending_job_data_cache.take();
|
||||
}
|
||||
|
||||
async fn wait_for_completions(self: Rc<Self>) {
|
||||
let mut buf = TypedBuf::<u64>::new();
|
||||
loop {
|
||||
if let Err(e) = self.ring.read(&self.have_completed_jobs, buf.buf()).await {
|
||||
log::error!("Could not wait for job completions: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
self.dispatch_completions();
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_completions(&self) {
|
||||
let completions = &mut *self.completed_jobs_local.borrow_mut();
|
||||
mem::swap(completions, &mut self.completed_jobs_remote.lock().queue);
|
||||
while let Some(id) = completions.pop_front() {
|
||||
let job_data = self.pending_jobs.remove(&id).unwrap();
|
||||
let job = job_data.job.take().unwrap();
|
||||
let job = unsafe { Box::from_raw(job.as_ptr()) };
|
||||
match job_data.state.get() {
|
||||
PendingJobState::Waiting => {
|
||||
job_data.state.set(PendingJobState::Completed);
|
||||
job.completed();
|
||||
}
|
||||
PendingJobState::Abandoned => {
|
||||
self.pending_job_data_cache.push(job_data);
|
||||
}
|
||||
PendingJobState::Completed => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn equeue_jobs(self: Rc<Self>) {
|
||||
loop {
|
||||
self.jobs_to_enqueue.non_empty().await;
|
||||
self.do_equeue_jobs();
|
||||
}
|
||||
}
|
||||
|
||||
fn do_equeue_jobs(&self) {
|
||||
self.jobs_to_enqueue.move_to(&mut self.new_jobs.lock());
|
||||
if let Err(e) = uapi::eventfd_write(self.have_new_jobs.raw(), 1) {
|
||||
panic!("Could not signal eventfd: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWorker {
|
||||
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Result<Self, CpuWorkerError> {
|
||||
let new_jobs: Arc<Mutex<VecDeque<Job>>> = Default::default();
|
||||
let completed_jobs: Arc<Mutex<CompletedJobsExchange>> = Default::default();
|
||||
let Pipe {
|
||||
read: stop_read,
|
||||
write: stop_write,
|
||||
} = pipe().map_err(CpuWorkerError::Pipe)?;
|
||||
let have_new_jobs = uapi::eventfd(0, c::EFD_CLOEXEC).map_os_err(CpuWorkerError::EventFd)?;
|
||||
let have_completed_jobs =
|
||||
uapi::eventfd(0, c::EFD_CLOEXEC).map_os_err(CpuWorkerError::EventFd)?;
|
||||
thread::Builder::new()
|
||||
.name("cpu worker".to_string())
|
||||
.spawn({
|
||||
let new_jobs = new_jobs.clone();
|
||||
let completed_jobs = completed_jobs.clone();
|
||||
let have_new_jobs = uapi::fcntl_dupfd_cloexec(have_new_jobs.raw(), 0)
|
||||
.map_os_err(CpuWorkerError::Dup)?;
|
||||
let have_completed_jobs = uapi::fcntl_dupfd_cloexec(have_completed_jobs.raw(), 0)
|
||||
.map_os_err(CpuWorkerError::Dup)?;
|
||||
move || {
|
||||
work(
|
||||
new_jobs,
|
||||
completed_jobs,
|
||||
stop_write,
|
||||
have_new_jobs,
|
||||
have_completed_jobs,
|
||||
)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let data = Rc::new(CpuWorkerData {
|
||||
next: Default::default(),
|
||||
jobs_to_enqueue: Default::default(),
|
||||
new_jobs,
|
||||
have_new_jobs: Rc::new(have_new_jobs),
|
||||
completed_jobs_remote: completed_jobs,
|
||||
completed_jobs_local: Default::default(),
|
||||
have_completed_jobs: Rc::new(have_completed_jobs),
|
||||
pending_jobs: Default::default(),
|
||||
ring: ring.clone(),
|
||||
_stop: stop_read,
|
||||
pending_job_data_cache: Default::default(),
|
||||
sync_wake_condvar: Arc::new(Condvar::new()),
|
||||
});
|
||||
Ok(Self {
|
||||
_completions_listener: eng.spawn(
|
||||
"cpu worker completions",
|
||||
data.clone().wait_for_completions(),
|
||||
),
|
||||
_job_enqueuer: eng.spawn("cpu worker enqueue", data.clone().equeue_jobs()),
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.data.clear();
|
||||
}
|
||||
|
||||
pub fn submit(&self, job: Box<dyn CpuJob>) -> PendingJob {
|
||||
let mut job = NonNull::from(Box::leak(job));
|
||||
let id = self.data.next.next();
|
||||
self.data.jobs_to_enqueue.push(Job::New {
|
||||
id,
|
||||
work: unsafe { job.as_mut().work() },
|
||||
});
|
||||
let job_data = self.data.pending_job_data_cache.pop().unwrap_or_default();
|
||||
job_data.job.set(Some(job));
|
||||
job_data.state.set(PendingJobState::Waiting);
|
||||
self.data.pending_jobs.set(id, job_data.clone());
|
||||
PendingJob {
|
||||
id,
|
||||
thread_data: self.data.clone(),
|
||||
job_data,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "it")]
|
||||
pub fn wait_idle(&self) -> bool {
|
||||
let was_idle = self.data.pending_jobs.is_empty();
|
||||
loop {
|
||||
self.data.dispatch_completions();
|
||||
if self.data.pending_jobs.is_empty() {
|
||||
break;
|
||||
}
|
||||
let mut remote = self.data.completed_jobs_remote.lock();
|
||||
while remote.queue.is_empty() {
|
||||
remote.condvar = Some(self.data.sync_wake_condvar.clone());
|
||||
self.data.sync_wake_condvar.wait(&mut remote);
|
||||
}
|
||||
}
|
||||
was_idle
|
||||
}
|
||||
}
|
||||
|
||||
fn work(
|
||||
new_jobs: Arc<Mutex<VecDeque<Job>>>,
|
||||
completed_jobs: Arc<Mutex<CompletedJobsExchange>>,
|
||||
stop: OwnedFd,
|
||||
have_new_jobs: OwnedFd,
|
||||
have_completed_jobs: OwnedFd,
|
||||
) {
|
||||
let eng = AsyncEngine::new();
|
||||
let ring = IoUring::new(&eng, 32).unwrap();
|
||||
let worker = Rc::new(Worker {
|
||||
eng,
|
||||
ring,
|
||||
completed_jobs,
|
||||
have_completed_jobs,
|
||||
async_jobs: Default::default(),
|
||||
stopped: Cell::new(false),
|
||||
});
|
||||
let _stop_listener = worker
|
||||
.eng
|
||||
.spawn("stop listener", worker.clone().handle_stop(stop));
|
||||
let _new_job_listener = worker.eng.spawn(
|
||||
"new job listener",
|
||||
worker.clone().handle_new_jobs(new_jobs, have_new_jobs),
|
||||
);
|
||||
if let Err(e) = worker.ring.run() {
|
||||
panic!("io_uring failed: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
eng: Rc<AsyncEngine>,
|
||||
ring: Rc<IoUring>,
|
||||
completed_jobs: Arc<Mutex<CompletedJobsExchange>>,
|
||||
have_completed_jobs: OwnedFd,
|
||||
async_jobs: CopyHashMap<CpuJobId, AsyncJob>,
|
||||
stopped: Cell<bool>,
|
||||
}
|
||||
|
||||
struct AsyncJob {
|
||||
_future: SpawnedFuture<CompletedWork>,
|
||||
work: *mut dyn CpuWork,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
async fn handle_stop(self: Rc<Self>, stop: OwnedFd) {
|
||||
let stop = Rc::new(stop);
|
||||
if let Err(e) = self.ring.poll(&stop, 0).await {
|
||||
log::error!(
|
||||
"Could not wait for stop fd to become readable: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
} else {
|
||||
assert!(self.async_jobs.is_empty());
|
||||
self.stopped.set(true);
|
||||
self.ring.stop();
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_new_jobs(
|
||||
self: Rc<Self>,
|
||||
jobs_remote: Arc<Mutex<VecDeque<Job>>>,
|
||||
new_jobs: OwnedFd,
|
||||
) {
|
||||
let mut buf = TypedBuf::<u64>::new();
|
||||
let new_jobs = Rc::new(new_jobs);
|
||||
let mut jobs = VecDeque::new();
|
||||
loop {
|
||||
if let Err(e) = self.ring.read(&new_jobs, buf.buf()).await {
|
||||
if self.stopped.get() {
|
||||
return;
|
||||
}
|
||||
panic!(
|
||||
"Could not wait for new jobs fd to be signaled: {}",
|
||||
ErrorFmt(e),
|
||||
);
|
||||
}
|
||||
mem::swap(&mut jobs, &mut *jobs_remote.lock());
|
||||
while let Some(job) = jobs.pop_front() {
|
||||
self.handle_new_job(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_new_job(self: &Rc<Self>, job: Job) {
|
||||
match job {
|
||||
Job::Cancel { id } => {
|
||||
let mut jobs = self.async_jobs.lock();
|
||||
if let Some(job) = jobs.get_mut(&id) {
|
||||
unsafe {
|
||||
job.work.deref_mut().cancel_async(&self.ring);
|
||||
}
|
||||
}
|
||||
}
|
||||
Job::New { id, work } => match unsafe { work.deref_mut() }.run() {
|
||||
None => {
|
||||
self.send_completion(id);
|
||||
return;
|
||||
}
|
||||
Some(w) => {
|
||||
let completion = WorkCompletion {
|
||||
worker: self.clone(),
|
||||
id,
|
||||
};
|
||||
let future = w.run(&self.eng, &self.ring, completion);
|
||||
self.async_jobs.set(
|
||||
id,
|
||||
AsyncJob {
|
||||
_future: future,
|
||||
work,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn send_completion(&self, id: CpuJobId) {
|
||||
let cv = {
|
||||
let mut exchange = self.completed_jobs.lock();
|
||||
exchange.queue.push_back(id);
|
||||
exchange.condvar.take()
|
||||
};
|
||||
if let Some(cv) = cv {
|
||||
cv.notify_all();
|
||||
}
|
||||
if let Err(e) = uapi::eventfd_write(self.have_completed_jobs.raw(), 1) {
|
||||
panic!("Could not signal job completion: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use jay_cpu_worker::*;
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
pub mod img_copy;
|
||||
pub mod read_write;
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
cpu_worker::{AsyncCpuWork, CpuWork},
|
||||
rect::Rect,
|
||||
},
|
||||
std::ptr,
|
||||
};
|
||||
|
||||
pub struct ImgCopyWork {
|
||||
pub src: *mut u8,
|
||||
pub dst: *mut u8,
|
||||
pub width: i32,
|
||||
pub stride: i32,
|
||||
pub bpp: i32,
|
||||
pub rects: Vec<Rect>,
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
unsafe impl Send for ImgCopyWork {}
|
||||
|
||||
impl ImgCopyWork {
|
||||
pub unsafe fn new() -> Self {
|
||||
Self {
|
||||
src: ptr::null_mut(),
|
||||
dst: ptr::null_mut(),
|
||||
width: 0,
|
||||
stride: 0,
|
||||
bpp: 0,
|
||||
rects: vec![],
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWork for ImgCopyWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
zone!("ImgCopyWork");
|
||||
for rect in &self.rects {
|
||||
let mut offset = rect.y1() * self.stride + rect.x1() * self.bpp;
|
||||
if rect.width() == self.width {
|
||||
let offset = offset as usize;
|
||||
let len = rect.height() * self.stride;
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(self.src.add(offset), self.dst.add(offset), len as _);
|
||||
}
|
||||
} else {
|
||||
let len = rect.width() * self.bpp;
|
||||
for _ in 0..rect.height() {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
self.src.add(offset as _),
|
||||
self.dst.add(offset as _),
|
||||
len as _,
|
||||
);
|
||||
}
|
||||
offset += self.stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cpu_worker::{AsyncCpuWork, CompletedWork, CpuWork, WorkCompletion},
|
||||
io_uring::{IoUring, IoUringError, IoUringTaskId},
|
||||
},
|
||||
std::{
|
||||
any::Any,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicU64, Ordering::Relaxed},
|
||||
},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{Fd, c},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ReadWriteJobError {
|
||||
#[error("An io_uring error occurred")]
|
||||
IoUring(#[source] IoUringError),
|
||||
#[error("The job was cancelled")]
|
||||
Cancelled,
|
||||
#[error("Tried to operate outside the bounds of the file descriptor")]
|
||||
OutOfBounds,
|
||||
}
|
||||
|
||||
pub struct ReadWriteWork {
|
||||
cancel: Arc<CancelState>,
|
||||
config: Option<Box<ReadWriteWorkConfig>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ReadWriteWork {}
|
||||
|
||||
impl ReadWriteWork {
|
||||
pub unsafe fn new() -> Self {
|
||||
let cancel = Arc::new(CancelState::default());
|
||||
ReadWriteWork {
|
||||
cancel: cancel.clone(),
|
||||
config: Some(Box::new(ReadWriteWorkConfig {
|
||||
fd: -1,
|
||||
offset: 0,
|
||||
ptr: ptr::null_mut(),
|
||||
len: 0,
|
||||
write: false,
|
||||
cancel,
|
||||
result: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(&mut self) -> &mut ReadWriteWorkConfig {
|
||||
self.config.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadWriteWorkConfig {
|
||||
pub fd: c::c_int,
|
||||
pub offset: usize,
|
||||
pub ptr: *mut u8,
|
||||
pub len: usize,
|
||||
pub write: bool,
|
||||
pub result: Option<Result<(), ReadWriteJobError>>,
|
||||
cancel: Arc<CancelState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CancelState {
|
||||
cancelled: AtomicBool,
|
||||
cancel_id: AtomicU64,
|
||||
}
|
||||
|
||||
impl CpuWork for ReadWriteWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
self.cancel.cancelled.store(false, Relaxed);
|
||||
self.cancel.cancel_id.store(0, Relaxed);
|
||||
self.config.take().map(|b| b as _)
|
||||
}
|
||||
|
||||
fn cancel_async(&mut self, ring: &Rc<IoUring>) {
|
||||
self.cancel.cancelled.store(true, Relaxed);
|
||||
let id = self.cancel.cancel_id.load(Relaxed);
|
||||
if id != 0 {
|
||||
ring.cancel(IoUringTaskId::from_raw(id));
|
||||
}
|
||||
}
|
||||
|
||||
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||
let work = (work as Box<dyn Any>).downcast().unwrap();
|
||||
self.config = Some(work);
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncCpuWork for ReadWriteWorkConfig {
|
||||
fn run(
|
||||
mut self: Box<Self>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
completion: WorkCompletion,
|
||||
) -> SpawnedFuture<CompletedWork> {
|
||||
let ring = ring.clone();
|
||||
eng.spawn("shm read/write", async move {
|
||||
let res = loop {
|
||||
if self.cancel.cancelled.load(Relaxed) {
|
||||
break Err(ReadWriteJobError::Cancelled);
|
||||
}
|
||||
if self.len == 0 {
|
||||
break Ok(());
|
||||
};
|
||||
let res = if self.write {
|
||||
ring.write_no_cancel(
|
||||
Fd::new(self.fd),
|
||||
self.offset,
|
||||
unsafe { slice::from_raw_parts(self.ptr, self.len) },
|
||||
None,
|
||||
|id| self.cancel.cancel_id.store(id.raw(), Relaxed),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
ring.read_no_cancel(
|
||||
Fd::new(self.fd),
|
||||
self.offset,
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) },
|
||||
|id| self.cancel.cancel_id.store(id.raw(), Relaxed),
|
||||
)
|
||||
.await
|
||||
};
|
||||
match res {
|
||||
Ok(0) => break Err(ReadWriteJobError::OutOfBounds),
|
||||
Ok(n) => {
|
||||
self.len -= n;
|
||||
self.offset += n;
|
||||
unsafe {
|
||||
self.ptr = self.ptr.add(n);
|
||||
}
|
||||
}
|
||||
Err(e) => break Err(ReadWriteJobError::IoUring(e)),
|
||||
}
|
||||
};
|
||||
self.result = Some(res);
|
||||
completion.complete(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cpu_worker::{AsyncCpuWork, CompletedWork, CpuJob, CpuWork, CpuWorker, WorkCompletion},
|
||||
io_uring::IoUring,
|
||||
utils::asyncevent::AsyncEvent,
|
||||
wheel::Wheel,
|
||||
},
|
||||
std::{future::pending, rc::Rc, sync::Arc},
|
||||
uapi::{OwnedFd, c::EFD_CLOEXEC},
|
||||
};
|
||||
|
||||
struct Job {
|
||||
ae: Rc<AsyncEvent>,
|
||||
work: Work,
|
||||
cancel: bool,
|
||||
}
|
||||
struct Work(Arc<OwnedFd>);
|
||||
struct AsyncWork(Arc<OwnedFd>);
|
||||
|
||||
impl CpuJob for Job {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
&mut self.work
|
||||
}
|
||||
|
||||
fn completed(self: Box<Self>) {
|
||||
if self.cancel {
|
||||
unreachable!();
|
||||
} else {
|
||||
self.ae.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Job {
|
||||
fn drop(&mut self) {
|
||||
if self.cancel {
|
||||
self.ae.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWork for Work {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
Some(Box::new(AsyncWork(self.0.clone())))
|
||||
}
|
||||
|
||||
fn cancel_async(&mut self, _ring: &Rc<IoUring>) {
|
||||
uapi::eventfd_write(self.0.raw(), 1).unwrap();
|
||||
}
|
||||
|
||||
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||
let _ = work;
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncCpuWork for AsyncWork {
|
||||
fn run(
|
||||
self: Box<Self>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
completion: WorkCompletion,
|
||||
) -> SpawnedFuture<CompletedWork> {
|
||||
let ring = ring.clone();
|
||||
eng.spawn("", async move {
|
||||
let mut buf = [0; 8];
|
||||
let res = ring
|
||||
.read_no_cancel(self.0.borrow(), 0, &mut buf, |_| ())
|
||||
.await;
|
||||
res.unwrap();
|
||||
completion.complete(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cancel: bool) {
|
||||
let eng = AsyncEngine::new();
|
||||
let ring = IoUring::new(&eng, 32).unwrap();
|
||||
let ring2 = ring.clone();
|
||||
let wheel = Wheel::new(&eng, &ring).unwrap();
|
||||
let cpu = Rc::new(CpuWorker::new(&ring, &eng).unwrap());
|
||||
let ae = Rc::new(AsyncEvent::default());
|
||||
let eventfd = Arc::new(uapi::eventfd(0, EFD_CLOEXEC).unwrap());
|
||||
let pending_job = cpu.submit(Box::new(Job {
|
||||
ae: ae.clone(),
|
||||
work: Work(eventfd.clone()),
|
||||
cancel,
|
||||
}));
|
||||
let _fut1 = eng.spawn("", async move {
|
||||
wheel.timeout(1).await.unwrap();
|
||||
if cancel {
|
||||
drop(pending_job);
|
||||
} else {
|
||||
uapi::eventfd_write(eventfd.raw(), 1).unwrap();
|
||||
pending::<()>().await;
|
||||
}
|
||||
});
|
||||
let _fut2 = eng.spawn("", async move {
|
||||
ae.triggered().await;
|
||||
ring2.stop();
|
||||
});
|
||||
ring.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel() {
|
||||
run(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete() {
|
||||
run(false);
|
||||
}
|
||||
|
|
@ -1,96 +1,4 @@
|
|||
pub mod clm;
|
||||
mod crit_graph;
|
||||
pub mod crit_leaf;
|
||||
mod crit_matchers;
|
||||
mod crit_per_target_data;
|
||||
pub mod tlm;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
criteria::{
|
||||
crit_graph::{CritMgr, CritMiddle, CritRoot, CritRootCriterion, CritRootFixed},
|
||||
crit_leaf::CritLeafMatcher,
|
||||
crit_matchers::{critm_any_or_all::CritMatchAnyOrAll, critm_exactly::CritMatchExactly},
|
||||
},
|
||||
utils::copyhashmap::CopyHashMap,
|
||||
},
|
||||
linearize::StaticMap,
|
||||
regex::Regex,
|
||||
std::rc::{Rc, Weak},
|
||||
};
|
||||
pub use {
|
||||
crit_graph::{CritTarget, CritUpstreamNode},
|
||||
crit_per_target_data::CritDestroyListener,
|
||||
};
|
||||
|
||||
linear_ids!(CritMatcherIds, CritMatcherId, u64);
|
||||
|
||||
type RootMatcherMap<Target, T> = CopyHashMap<CritMatcherId, Weak<CritRoot<Target, T>>>;
|
||||
type FixedRootMatcher<Target, T> = StaticMap<bool, Rc<CritRoot<Target, CritRootFixed<Target, T>>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CritLiteralOrRegex {
|
||||
Literal(String),
|
||||
Regex(Regex),
|
||||
}
|
||||
|
||||
impl CritLiteralOrRegex {
|
||||
fn matches(&self, string: &str) -> bool {
|
||||
match self {
|
||||
CritLiteralOrRegex::Literal(p) => string == p,
|
||||
CritLiteralOrRegex::Regex(r) => r.is_match(string),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CritMgrExt: CritMgr {
|
||||
fn list(
|
||||
&self,
|
||||
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||
all: bool,
|
||||
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||
if upstream.is_empty() {
|
||||
return self.match_constant()[all].clone();
|
||||
}
|
||||
CritMiddle::new(self, upstream, CritMatchAnyOrAll::new(upstream, all))
|
||||
}
|
||||
|
||||
fn exactly(
|
||||
&self,
|
||||
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||
num: usize,
|
||||
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||
if num > upstream.len() {
|
||||
return self.match_constant()[false].clone();
|
||||
}
|
||||
if num == 0 {
|
||||
let upstream: Vec<_> = upstream.iter().map(|u| u.not(self)).collect();
|
||||
return self.list(&upstream, true);
|
||||
}
|
||||
CritMiddle::new(self, upstream, CritMatchExactly::new(upstream, num))
|
||||
}
|
||||
|
||||
fn leaf(
|
||||
&self,
|
||||
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||
on_match: impl Fn(<Self::Target as CritTarget>::LeafData) -> Box<dyn FnOnce()> + 'static,
|
||||
) -> Rc<CritLeafMatcher<Self::Target>> {
|
||||
CritLeafMatcher::new(self, upstream, on_match)
|
||||
}
|
||||
|
||||
fn not(
|
||||
&self,
|
||||
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||
upstream.not(self)
|
||||
}
|
||||
|
||||
fn root<T>(&self, criterion: T) -> Rc<dyn CritUpstreamNode<Self::Target>>
|
||||
where
|
||||
T: CritRootCriterion<Self::Target>,
|
||||
{
|
||||
CritRoot::new(self.roots(), self.id(), criterion)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CritMgrExt for T where T: CritMgr {}
|
||||
pub use jay_criteria::*;
|
||||
|
|
|
|||
|
|
@ -39,19 +39,19 @@ bitflags! {
|
|||
CL_CHANGED_NEW,
|
||||
}
|
||||
|
||||
type ClmFixedRootMatcher<T> = FixedRootMatcher<Rc<Client>, T>;
|
||||
type ClmFixedRootMatcher<T> = FixedRootMatcher<Client, T>;
|
||||
|
||||
pub struct ClMatcherManager {
|
||||
ids: Rc<CritMatcherIds>,
|
||||
changes: AsyncQueue<Rc<Client>>,
|
||||
leaf_events: Rc<AsyncQueue<CritLeafEvent<Rc<Client>>>>,
|
||||
constant: ClmFixedRootMatcher<CritMatchConstant<Rc<Client>>>,
|
||||
leaf_events: Rc<AsyncQueue<CritLeafEvent<Client>>>,
|
||||
constant: ClmFixedRootMatcher<CritMatchConstant<Client>>,
|
||||
sandboxed: ClmFixedRootMatcher<ClmMatchSandboxed>,
|
||||
is_xwayland: ClmFixedRootMatcher<ClmMatchIsXwayland>,
|
||||
matchers: Rc<RootMatchers>,
|
||||
}
|
||||
|
||||
type ClmRootMatcherMap<T> = RootMatcherMap<Rc<Client>, T>;
|
||||
type ClmRootMatcherMap<T> = RootMatcherMap<Client, T>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RootMatchers {
|
||||
|
|
@ -98,8 +98,8 @@ pub async fn handle_cl_leaf_events(state: Rc<State>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub type ClmUpstreamNode = dyn CritUpstreamNode<Rc<Client>>;
|
||||
pub type ClmLeafMatcher = CritLeafMatcher<Rc<Client>>;
|
||||
pub type ClmUpstreamNode = dyn CritUpstreamNode<Client>;
|
||||
pub type ClmLeafMatcher = CritLeafMatcher<Client>;
|
||||
|
||||
impl ClMatcherManager {
|
||||
pub fn new(ids: &Rc<CritMatcherIds>) -> Self {
|
||||
|
|
@ -146,6 +146,7 @@ impl ClMatcherManager {
|
|||
}
|
||||
|
||||
fn update_matches(&self, data: &Rc<Client>) {
|
||||
let data = data.as_ref();
|
||||
let mut changed = data.changed_properties.take();
|
||||
if changed.contains(CL_CHANGED_DESTROYED) {
|
||||
for destroyed in data.destroyed.lock().drain_values() {
|
||||
|
|
@ -235,15 +236,27 @@ impl ClMatcherManager {
|
|||
}
|
||||
}
|
||||
|
||||
impl CritTarget for Rc<Client> {
|
||||
pub struct ClientTargetOwner {
|
||||
client: Rc<Client>,
|
||||
}
|
||||
|
||||
pub struct WeakClientTargetOwner {
|
||||
state: Weak<State>,
|
||||
id: ClientId,
|
||||
}
|
||||
|
||||
impl CritTarget for Client {
|
||||
type Id = ClientId;
|
||||
type Mgr = ClMatcherManager;
|
||||
type RootMatchers = RootMatchers;
|
||||
type LeafData = ClientId;
|
||||
type Owner = Weak<Client>;
|
||||
type Owner = WeakClientTargetOwner;
|
||||
|
||||
fn owner(&self) -> Self::Owner {
|
||||
Rc::downgrade(self)
|
||||
WeakClientTargetOwner {
|
||||
state: Rc::downgrade(&self.state),
|
||||
id: self.id,
|
||||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
|
|
@ -259,25 +272,27 @@ impl CritTarget for Rc<Client> {
|
|||
}
|
||||
}
|
||||
|
||||
impl CritTargetOwner for Rc<Client> {
|
||||
type Target = Rc<Client>;
|
||||
impl CritTargetOwner for ClientTargetOwner {
|
||||
type Target = Client;
|
||||
|
||||
fn data(&self) -> &Self::Target {
|
||||
self
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakCritTargetOwner for Weak<Client> {
|
||||
type Target = Rc<Client>;
|
||||
type Owner = Rc<Client>;
|
||||
impl WeakCritTargetOwner for WeakClientTargetOwner {
|
||||
type Target = Client;
|
||||
type Owner = ClientTargetOwner;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Owner> {
|
||||
self.upgrade()
|
||||
let state = self.state.upgrade()?;
|
||||
let client = state.clients.get(self.id).ok()?;
|
||||
Some(ClientTargetOwner { client })
|
||||
}
|
||||
}
|
||||
|
||||
impl CritMgr for ClMatcherManager {
|
||||
type Target = Rc<Client>;
|
||||
type Target = Client;
|
||||
|
||||
fn id(&self) -> CritMatcherId {
|
||||
self.ids.next()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
macro_rules! fixed_root_criterion {
|
||||
($ty:ty, $field:ident) => {
|
||||
impl crate::criteria::crit_graph::CritFixedRootCriterionBase<Rc<crate::client::Client>>
|
||||
impl crate::criteria::crit_graph::CritFixedRootCriterionBase<crate::client::Client>
|
||||
for $ty
|
||||
{
|
||||
fn constant(&self) -> bool {
|
||||
|
|
@ -10,7 +10,7 @@ macro_rules! fixed_root_criterion {
|
|||
fn not<'a>(
|
||||
&self,
|
||||
mgr: &'a crate::criteria::clm::ClMatcherManager,
|
||||
) -> &'a crate::criteria::FixedRootMatcher<Rc<crate::client::Client>, Self> {
|
||||
) -> &'a crate::criteria::FixedRootMatcher<crate::client::Client, Self> {
|
||||
&mgr.$field
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@ use {
|
|||
client::{Client, ClientId},
|
||||
criteria::{RootMatcherMap, clm::RootMatchers, crit_graph::CritRootCriterion},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct ClmMatchId(pub ClientId);
|
||||
|
||||
impl CritRootCriterion<Rc<Client>> for ClmMatchId {
|
||||
fn matches(&self, data: &Rc<Client>) -> bool {
|
||||
impl CritRootCriterion<Client> for ClmMatchId {
|
||||
fn matches(&self, data: &Client) -> bool {
|
||||
data.id == self.0
|
||||
}
|
||||
|
||||
fn nodes(roots: &RootMatchers) -> Option<&RootMatcherMap<Rc<Client>, Self>> {
|
||||
fn nodes(roots: &RootMatchers) -> Option<&RootMatcherMap<Client, Self>> {
|
||||
Some(&roots.id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
use {
|
||||
crate::{client::Client, criteria::crit_graph::CritFixedRootCriterion},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct ClmMatchIsXwayland(pub bool);
|
||||
|
||||
fixed_root_criterion!(ClmMatchIsXwayland, is_xwayland);
|
||||
|
||||
impl CritFixedRootCriterion<Rc<Client>> for ClmMatchIsXwayland {
|
||||
fn matches(&self, data: &Rc<Client>) -> bool {
|
||||
impl CritFixedRootCriterion<Client> for ClmMatchIsXwayland {
|
||||
fn matches(&self, data: &Client) -> bool {
|
||||
data.is_xwayland
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,17 @@ use {
|
|||
client::Client,
|
||||
criteria::{RootMatcherMap, clm::RootMatchers, crit_graph::CritRootCriterion},
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub struct ClmMatchPid(pub c::pid_t);
|
||||
|
||||
impl CritRootCriterion<Rc<Client>> for ClmMatchPid {
|
||||
fn matches(&self, data: &Rc<Client>) -> bool {
|
||||
impl CritRootCriterion<Client> for ClmMatchPid {
|
||||
fn matches(&self, data: &Client) -> bool {
|
||||
data.pid_info.pid == self.0
|
||||
}
|
||||
|
||||
fn nodes(roots: &RootMatchers) -> Option<&RootMatcherMap<Rc<Client>, Self>> {
|
||||
fn nodes(roots: &RootMatchers) -> Option<&RootMatcherMap<Client, Self>> {
|
||||
Some(&roots.pid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
use {
|
||||
crate::{client::Client, criteria::crit_graph::CritFixedRootCriterion},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct ClmMatchSandboxed(pub bool);
|
||||
|
||||
fixed_root_criterion!(ClmMatchSandboxed, sandboxed);
|
||||
|
||||
impl CritFixedRootCriterion<Rc<Client>> for ClmMatchSandboxed {
|
||||
fn matches(&self, data: &Rc<Client>) -> bool {
|
||||
data.acceptor.sandboxed
|
||||
impl CritFixedRootCriterion<Client> for ClmMatchSandboxed {
|
||||
fn matches(&self, data: &Client) -> bool {
|
||||
data.metadata.sandboxed
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,32 @@
|
|||
use {
|
||||
crate::{
|
||||
client::Client,
|
||||
client::{Client, ClientMetadata},
|
||||
criteria::{
|
||||
clm::{ClmRootMatcherMap, RootMatchers},
|
||||
crit_matchers::critm_string::{CritMatchString, StringAccess},
|
||||
},
|
||||
security_context_acceptor::AcceptorMetadata,
|
||||
},
|
||||
std::{marker::PhantomData, rc::Rc},
|
||||
std::marker::PhantomData,
|
||||
};
|
||||
|
||||
pub type ClmMatchString<T> = CritMatchString<Rc<Client>, T>;
|
||||
pub type ClmMatchString<T> = CritMatchString<Client, T>;
|
||||
|
||||
pub type ClmMatchSandboxEngine = ClmMatchString<AcceptorMetadataAccess<SandboxEngineField>>;
|
||||
pub type ClmMatchSandboxAppId = ClmMatchString<AcceptorMetadataAccess<SandboxAppIdField>>;
|
||||
pub type ClmMatchSandboxInstanceId = ClmMatchString<AcceptorMetadataAccess<SandboxInstanceIdField>>;
|
||||
pub type ClmMatchTag = ClmMatchString<AcceptorMetadataAccess<TagField>>;
|
||||
pub type ClmMatchSandboxEngine = ClmMatchString<ClientMetadataAccess<SandboxEngineField>>;
|
||||
pub type ClmMatchSandboxAppId = ClmMatchString<ClientMetadataAccess<SandboxAppIdField>>;
|
||||
pub type ClmMatchSandboxInstanceId = ClmMatchString<ClientMetadataAccess<SandboxInstanceIdField>>;
|
||||
pub type ClmMatchTag = ClmMatchString<ClientMetadataAccess<TagField>>;
|
||||
pub type ClmMatchComm = ClmMatchString<CommAccess>;
|
||||
pub type ClmMatchExe = ClmMatchString<ExeAccess>;
|
||||
|
||||
pub struct AcceptorMetadataAccess<T>(PhantomData<T>);
|
||||
pub struct ClientMetadataAccess<T>(PhantomData<T>);
|
||||
pub struct CommAccess;
|
||||
pub struct ExeAccess;
|
||||
|
||||
trait AcceptorMetadataField: Sized + 'static {
|
||||
fn field(meta: &AcceptorMetadata) -> &Option<String>;
|
||||
trait ClientMetadataField: Sized + 'static {
|
||||
fn field(meta: &ClientMetadata) -> &Option<String>;
|
||||
fn nodes(
|
||||
roots: &RootMatchers,
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<AcceptorMetadataAccess<Self>>>;
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<ClientMetadataAccess<Self>>>;
|
||||
}
|
||||
|
||||
pub struct SandboxEngineField;
|
||||
|
|
@ -35,12 +34,12 @@ pub struct SandboxAppIdField;
|
|||
pub struct SandboxInstanceIdField;
|
||||
pub struct TagField;
|
||||
|
||||
impl<T> StringAccess<Rc<Client>> for AcceptorMetadataAccess<T>
|
||||
impl<T> StringAccess<Client> for ClientMetadataAccess<T>
|
||||
where
|
||||
T: AcceptorMetadataField,
|
||||
T: ClientMetadataField,
|
||||
{
|
||||
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
||||
f(T::field(&data.acceptor).as_deref().unwrap_or_default())
|
||||
fn with_string(data: &Client, f: impl FnOnce(&str) -> bool) -> bool {
|
||||
f(T::field(&data.metadata).as_deref().unwrap_or_default())
|
||||
}
|
||||
|
||||
fn nodes(roots: &RootMatchers) -> &ClmRootMatcherMap<ClmMatchString<Self>> {
|
||||
|
|
@ -48,56 +47,56 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl AcceptorMetadataField for SandboxEngineField {
|
||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||
impl ClientMetadataField for SandboxEngineField {
|
||||
fn field(meta: &ClientMetadata) -> &Option<String> {
|
||||
&meta.sandbox_engine
|
||||
}
|
||||
|
||||
fn nodes(
|
||||
roots: &RootMatchers,
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<AcceptorMetadataAccess<Self>>> {
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<ClientMetadataAccess<Self>>> {
|
||||
&roots.sandbox_engine
|
||||
}
|
||||
}
|
||||
|
||||
impl AcceptorMetadataField for SandboxAppIdField {
|
||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||
impl ClientMetadataField for SandboxAppIdField {
|
||||
fn field(meta: &ClientMetadata) -> &Option<String> {
|
||||
&meta.app_id
|
||||
}
|
||||
|
||||
fn nodes(
|
||||
roots: &RootMatchers,
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<AcceptorMetadataAccess<Self>>> {
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<ClientMetadataAccess<Self>>> {
|
||||
&roots.sandbox_app_id
|
||||
}
|
||||
}
|
||||
|
||||
impl AcceptorMetadataField for SandboxInstanceIdField {
|
||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||
impl ClientMetadataField for SandboxInstanceIdField {
|
||||
fn field(meta: &ClientMetadata) -> &Option<String> {
|
||||
&meta.instance_id
|
||||
}
|
||||
|
||||
fn nodes(
|
||||
roots: &RootMatchers,
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<AcceptorMetadataAccess<Self>>> {
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<ClientMetadataAccess<Self>>> {
|
||||
&roots.sandbox_instance_id
|
||||
}
|
||||
}
|
||||
|
||||
impl AcceptorMetadataField for TagField {
|
||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||
impl ClientMetadataField for TagField {
|
||||
fn field(meta: &ClientMetadata) -> &Option<String> {
|
||||
&meta.tag
|
||||
}
|
||||
|
||||
fn nodes(
|
||||
roots: &RootMatchers,
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<AcceptorMetadataAccess<Self>>> {
|
||||
) -> &ClmRootMatcherMap<ClmMatchString<ClientMetadataAccess<Self>>> {
|
||||
&roots.tag
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAccess<Rc<Client>> for CommAccess {
|
||||
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
||||
impl StringAccess<Client> for CommAccess {
|
||||
fn with_string(data: &Client, f: impl FnOnce(&str) -> bool) -> bool {
|
||||
f(&data.pid_info.comm)
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +105,8 @@ impl StringAccess<Rc<Client>> for CommAccess {
|
|||
}
|
||||
}
|
||||
|
||||
impl StringAccess<Rc<Client>> for ExeAccess {
|
||||
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
||||
impl StringAccess<Client> for ExeAccess {
|
||||
fn with_string(data: &Client, f: impl FnOnce(&str) -> bool) -> bool {
|
||||
f(&data.pid_info.exe)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,17 @@ use {
|
|||
client::Client,
|
||||
criteria::{RootMatcherMap, clm::RootMatchers, crit_graph::CritRootCriterion},
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub struct ClmMatchUid(pub c::uid_t);
|
||||
|
||||
impl CritRootCriterion<Rc<Client>> for ClmMatchUid {
|
||||
fn matches(&self, data: &Rc<Client>) -> bool {
|
||||
impl CritRootCriterion<Client> for ClmMatchUid {
|
||||
fn matches(&self, data: &Client) -> bool {
|
||||
data.pid_info.uid == self.0
|
||||
}
|
||||
|
||||
fn nodes(roots: &RootMatchers) -> Option<&RootMatcherMap<Rc<Client>, Self>> {
|
||||
fn nodes(roots: &RootMatchers) -> Option<&RootMatcherMap<Client, Self>> {
|
||||
Some(&roots.uid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
mod crit_downstream;
|
||||
mod crit_middle;
|
||||
mod crit_root;
|
||||
mod crit_target;
|
||||
mod crit_upstream;
|
||||
|
||||
pub use {
|
||||
crit_downstream::{CritDownstream, CritDownstreamData},
|
||||
crit_middle::{CritMiddle, CritMiddleCriterion},
|
||||
crit_root::{
|
||||
CritFixedRootCriterion, CritFixedRootCriterionBase, CritRoot, CritRootCriterion,
|
||||
CritRootFixed,
|
||||
},
|
||||
crit_target::{CritMgr, CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
||||
crit_upstream::{
|
||||
CritUpstreamData, CritUpstreamNode, CritUpstreamNodeBase, CritUpstreamNodeData,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
use {
|
||||
crate::criteria::{
|
||||
CritMatcherId,
|
||||
crit_graph::{CritTarget, crit_upstream::CritUpstreamNode},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct CritDownstreamData<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
id: CritMatcherId,
|
||||
pub(super) upstream: Vec<Rc<dyn CritUpstreamNode<Target>>>,
|
||||
}
|
||||
|
||||
pub trait CritDownstream<Target>: 'static {
|
||||
fn update_matched(self: Rc<Self>, target: &Target, matched: bool);
|
||||
}
|
||||
|
||||
impl<Target> CritDownstreamData<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub fn new(id: CritMatcherId, upstream: &[Rc<dyn CritUpstreamNode<Target>>]) -> Self {
|
||||
Self {
|
||||
id,
|
||||
upstream: upstream.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach(&self, slf: &Rc<impl CritDownstream<Target>>) {
|
||||
for upstream in &self.upstream {
|
||||
upstream.attach(self.id, slf.clone() as _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not(&self, mgr: &Target::Mgr) -> Vec<Rc<dyn CritUpstreamNode<Target>>> {
|
||||
self.upstream.iter().map(|n| n.not(mgr)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> Drop for CritDownstreamData<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
for el in &self.upstream {
|
||||
el.detach(self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
use {
|
||||
crate::criteria::{
|
||||
CritUpstreamNode,
|
||||
crit_graph::{
|
||||
CritDownstream, CritDownstreamData, CritTarget, CritUpstreamData,
|
||||
crit_target::CritMgr,
|
||||
crit_upstream::{CritUpstreamNodeBase, CritUpstreamNodeData},
|
||||
},
|
||||
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct CritMiddle<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritMiddleCriterion<Target>,
|
||||
{
|
||||
upstream: CritDownstreamData<Target>,
|
||||
downstream: CritUpstreamData<Target, CritMiddleData<T::Data>>,
|
||||
criterion: T,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CritMiddleData<T> {
|
||||
matches: usize,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub trait CritMiddleCriterion<Target>: Sized + 'static {
|
||||
type Data: Default;
|
||||
type Not: CritMiddleCriterion<Target>;
|
||||
|
||||
fn update_matched(&self, target: &Target, node: &mut Self::Data, matched: bool) -> bool;
|
||||
fn pull(&self, upstream: &[Rc<dyn CritUpstreamNode<Target>>], target: &Target) -> bool;
|
||||
fn not(&self) -> Self::Not;
|
||||
}
|
||||
|
||||
impl<Target, T> CritMiddle<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritMiddleCriterion<Target>,
|
||||
{
|
||||
pub fn new(
|
||||
mgr: &Target::Mgr,
|
||||
upstream: &[Rc<dyn CritUpstreamNode<Target>>],
|
||||
criterion: T,
|
||||
) -> Rc<Self> {
|
||||
let id = mgr.id();
|
||||
let slf = Rc::new_cyclic(|slf| Self {
|
||||
upstream: CritDownstreamData::new(id, upstream),
|
||||
downstream: CritUpstreamData::new(slf, id),
|
||||
criterion,
|
||||
});
|
||||
slf.upstream.attach(&slf);
|
||||
slf
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritDownstream<Target> for CritMiddle<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritMiddleCriterion<Target>,
|
||||
{
|
||||
fn update_matched(self: Rc<Self>, target: &Target, matched: bool) {
|
||||
let mut node = self.downstream.get_or_create(target);
|
||||
let change = self
|
||||
.criterion
|
||||
.update_matched(target, &mut node.data, matched);
|
||||
let matches = match matched {
|
||||
true => node.matches + 1,
|
||||
false => node.matches - 1,
|
||||
};
|
||||
node.matches = matches;
|
||||
self.downstream
|
||||
.update_matched(target, node, change, matches == 0);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritUpstreamNodeBase<Target> for CritMiddle<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritMiddleCriterion<Target>,
|
||||
{
|
||||
type Data = CritMiddleData<T::Data>;
|
||||
|
||||
fn data(&self) -> &CritUpstreamData<Target, Self::Data> {
|
||||
&self.downstream
|
||||
}
|
||||
|
||||
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>> {
|
||||
let upstream = self.upstream.not(mgr);
|
||||
CritMiddle::new(mgr, &upstream, self.criterion.not())
|
||||
}
|
||||
|
||||
fn pull(&self, target: &Target) -> bool {
|
||||
self.criterion.pull(&self.upstream.upstream, target)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritDestroyListenerBase<Target> for CritMiddle<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritMiddleCriterion<Target>,
|
||||
{
|
||||
type Data = CritUpstreamNodeData<Target, CritMiddleData<T::Data>>;
|
||||
|
||||
fn data(&self) -> &CritPerTargetData<Target, Self::Data> {
|
||||
&self.downstream.nodes
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
use {
|
||||
crate::criteria::{
|
||||
CritMatcherId, CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
|
||||
crit_graph::{
|
||||
CritTarget, CritUpstreamData,
|
||||
crit_target::CritMgr,
|
||||
crit_upstream::{CritUpstreamNodeBase, CritUpstreamNodeData},
|
||||
},
|
||||
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||
},
|
||||
std::{marker::PhantomData, rc::Rc},
|
||||
};
|
||||
|
||||
pub struct CritRoot<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritRootCriterion<Target>,
|
||||
{
|
||||
id: CritMatcherId,
|
||||
downstream: CritUpstreamData<Target, ()>,
|
||||
not: bool,
|
||||
criterion: Rc<T>,
|
||||
roots: Rc<Target::RootMatchers>,
|
||||
}
|
||||
|
||||
pub struct CritRootFixed<Target, Crit>(pub Crit, pub PhantomData<fn(&Target)>);
|
||||
|
||||
pub trait CritRootCriterion<Target>: Sized + 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn matches(&self, data: &Target) -> bool;
|
||||
fn nodes(roots: &Target::RootMatchers) -> Option<&RootMatcherMap<Target, Self>> {
|
||||
let _ = roots;
|
||||
None
|
||||
}
|
||||
fn not(&self, mgr: &Target::Mgr) -> Option<Rc<dyn CritUpstreamNode<Target>>> {
|
||||
let _ = mgr;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CritFixedRootCriterionBase<Target>: Sized + 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn constant(&self) -> bool;
|
||||
fn not<'a>(&self, mgr: &'a Target::Mgr) -> &'a FixedRootMatcher<Target, Self>
|
||||
where
|
||||
Self: CritFixedRootCriterion<Target>;
|
||||
}
|
||||
|
||||
pub trait CritFixedRootCriterion<Target>: CritFixedRootCriterionBase<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
const COMPARE: bool = true;
|
||||
|
||||
fn matches(&self, data: &Target) -> bool;
|
||||
}
|
||||
|
||||
impl<Target, T> CritRootCriterion<Target> for CritRootFixed<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritFixedRootCriterion<Target>,
|
||||
{
|
||||
fn matches(&self, data: &Target) -> bool {
|
||||
let mut res = self.0.matches(data);
|
||||
if T::COMPARE {
|
||||
res = res == self.0.constant();
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn not(&self, mgr: &Target::Mgr) -> Option<Rc<dyn CritUpstreamNode<Target>>> {
|
||||
Some(self.0.not(mgr)[!self.0.constant()].clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritUpstreamNodeBase<Target> for CritRoot<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritRootCriterion<Target>,
|
||||
{
|
||||
type Data = ();
|
||||
|
||||
fn data(&self) -> &CritUpstreamData<Target, Self::Data> {
|
||||
&self.downstream
|
||||
}
|
||||
|
||||
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>> {
|
||||
if let Some(node) = self.criterion.not(mgr) {
|
||||
return node;
|
||||
}
|
||||
let id = mgr.id();
|
||||
Self::new_(&self.roots, id, self.criterion.clone(), !self.not)
|
||||
}
|
||||
|
||||
fn pull(&self, target: &Target) -> bool {
|
||||
self.criterion.matches(target) ^ self.not
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritRoot<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritRootCriterion<Target>,
|
||||
{
|
||||
pub fn new(roots: &Rc<Target::RootMatchers>, id: CritMatcherId, criterion: T) -> Rc<Self> {
|
||||
Self::new_(roots, id, Rc::new(criterion), false)
|
||||
}
|
||||
|
||||
fn new_(
|
||||
roots: &Rc<Target::RootMatchers>,
|
||||
id: CritMatcherId,
|
||||
criterion: Rc<T>,
|
||||
not: bool,
|
||||
) -> Rc<Self> {
|
||||
let slf = Rc::new_cyclic(|slf| Self {
|
||||
id,
|
||||
downstream: CritUpstreamData::new(slf, id),
|
||||
not,
|
||||
criterion,
|
||||
roots: roots.clone(),
|
||||
});
|
||||
if let Some(nodes) = T::nodes(roots) {
|
||||
nodes.set(id, Rc::downgrade(&slf));
|
||||
}
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.downstream.clear();
|
||||
}
|
||||
|
||||
pub fn handle(&self, target: &Target) {
|
||||
let new = self.criterion.matches(target) ^ self.not;
|
||||
let node = match new {
|
||||
true => self.downstream.get_or_create(target),
|
||||
false => match self.downstream.get(target) {
|
||||
Some(n) => n,
|
||||
None => return,
|
||||
},
|
||||
};
|
||||
self.downstream.update_matched(target, node, new, !new);
|
||||
}
|
||||
|
||||
pub fn has_downstream(&self) -> bool {
|
||||
self.downstream.has_downstream()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritDestroyListenerBase<Target> for CritRoot<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritRootCriterion<Target>,
|
||||
{
|
||||
type Data = CritUpstreamNodeData<Target, ()>;
|
||||
|
||||
fn data(&self) -> &CritPerTargetData<Target, Self::Data> {
|
||||
&self.downstream.nodes
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> Drop for CritRoot<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritRootCriterion<Target>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if let Some(nodes) = T::nodes(&self.roots) {
|
||||
nodes.remove(&self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
criteria::{
|
||||
CritDestroyListener, CritMatcherId, FixedRootMatcher, crit_leaf::CritLeafEvent,
|
||||
crit_matchers::critm_constant::CritMatchConstant,
|
||||
},
|
||||
utils::{copyhashmap::CopyHashMap, queue::AsyncQueue},
|
||||
},
|
||||
std::{
|
||||
hash::Hash,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
};
|
||||
|
||||
pub trait CritMgr: 'static {
|
||||
type Target: CritTarget<Mgr = Self>;
|
||||
|
||||
fn id(&self) -> CritMatcherId;
|
||||
fn leaf_events(&self) -> &Rc<AsyncQueue<CritLeafEvent<Self::Target>>>;
|
||||
fn match_constant(&self) -> &FixedRootMatcher<Self::Target, CritMatchConstant<Self::Target>>;
|
||||
fn roots(&self) -> &Rc<<Self::Target as CritTarget>::RootMatchers>;
|
||||
}
|
||||
|
||||
pub trait CritTarget: 'static {
|
||||
type Id: Copy + Hash + Eq;
|
||||
type Mgr: CritMgr<Target = Self>;
|
||||
type RootMatchers;
|
||||
type LeafData: Copy + Eq;
|
||||
type Owner: WeakCritTargetOwner<Target = Self>;
|
||||
|
||||
fn owner(&self) -> Self::Owner;
|
||||
fn id(&self) -> Self::Id;
|
||||
fn destroyed(&self) -> &CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Self>>>;
|
||||
fn leaf_data(&self) -> Self::LeafData;
|
||||
}
|
||||
|
||||
pub trait CritTargetOwner: 'static {
|
||||
type Target: CritTarget;
|
||||
|
||||
fn data(&self) -> &Self::Target;
|
||||
}
|
||||
|
||||
pub trait WeakCritTargetOwner: 'static {
|
||||
type Target: CritTarget;
|
||||
type Owner: CritTargetOwner<Target = Self::Target>;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Owner>;
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
criteria::{
|
||||
CritDestroyListener, CritMatcherId,
|
||||
crit_graph::{
|
||||
WeakCritTargetOwner,
|
||||
crit_downstream::CritDownstream,
|
||||
crit_target::{CritTarget, CritTargetOwner},
|
||||
},
|
||||
crit_per_target_data::CritPerTargetData,
|
||||
},
|
||||
utils::copyhashmap::CopyHashMap,
|
||||
},
|
||||
std::{
|
||||
cell::RefMut,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct CritUpstreamData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
downstream: CopyHashMap<CritMatcherId, Weak<dyn CritDownstream<Target>>>,
|
||||
pub nodes: CritPerTargetData<Target, CritUpstreamNodeData<Target, T>>,
|
||||
}
|
||||
|
||||
pub struct CritUpstreamNodeData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
matched: bool,
|
||||
tl: Target::Owner,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub trait CritUpstreamNodeBase<Target>: 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Data;
|
||||
|
||||
fn data(&self) -> &CritUpstreamData<Target, Self::Data>;
|
||||
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>>;
|
||||
fn pull(&self, target: &Target) -> bool;
|
||||
}
|
||||
|
||||
pub trait CritUpstreamNode<Target>: 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn attach(&self, id: CritMatcherId, downstream: Rc<dyn CritDownstream<Target>>);
|
||||
fn detach(&self, id: CritMatcherId);
|
||||
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>>;
|
||||
fn pull(&self, target: &Target) -> bool;
|
||||
fn get(&self, target: &Target) -> bool;
|
||||
}
|
||||
|
||||
impl<Target, T> CritUpstreamNode<Target> for T
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritUpstreamNodeBase<Target>,
|
||||
{
|
||||
fn attach(&self, id: CritMatcherId, downstream: Rc<dyn CritDownstream<Target>>) {
|
||||
let data = self.data();
|
||||
for n in data.nodes.borrow_mut().values_mut() {
|
||||
if !n.matched {
|
||||
continue;
|
||||
}
|
||||
let Some(target) = n.tl.upgrade() else {
|
||||
continue;
|
||||
};
|
||||
downstream.clone().update_matched(target.data(), true);
|
||||
}
|
||||
data.downstream.set(id, Rc::downgrade(&downstream));
|
||||
}
|
||||
|
||||
fn detach(&self, id: CritMatcherId) {
|
||||
self.data().downstream.remove(&id);
|
||||
}
|
||||
|
||||
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>> {
|
||||
<T as CritUpstreamNodeBase<Target>>::not(self, mgr)
|
||||
}
|
||||
|
||||
fn pull(&self, target: &Target) -> bool {
|
||||
<T as CritUpstreamNodeBase<Target>>::pull(self, target)
|
||||
}
|
||||
|
||||
fn get(&self, target: &Target) -> bool {
|
||||
<T as CritUpstreamNodeBase<Target>>::data(self).matched(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> Deref for CritUpstreamNodeData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> DerefMut for CritUpstreamNodeData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritUpstreamData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub fn new(slf: &Weak<impl CritDestroyListener<Target>>, id: CritMatcherId) -> Self {
|
||||
Self {
|
||||
downstream: Default::default(),
|
||||
nodes: CritPerTargetData::new(slf, id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.nodes.clear()
|
||||
}
|
||||
|
||||
pub fn update_matched(
|
||||
&self,
|
||||
target: &Target,
|
||||
mut node: RefMut<CritUpstreamNodeData<Target, T>>,
|
||||
matched: bool,
|
||||
remove: bool,
|
||||
) {
|
||||
let unchanged = mem::replace(&mut node.matched, matched) == matched;
|
||||
drop(node);
|
||||
if remove {
|
||||
self.nodes.remove(target.id());
|
||||
}
|
||||
if unchanged {
|
||||
return;
|
||||
}
|
||||
for el in self.downstream.lock().values() {
|
||||
if let Some(el) = el.upgrade() {
|
||||
el.update_matched(target, matched);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_create(&self, target: &Target) -> RefMut<'_, CritUpstreamNodeData<Target, T>>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
self.nodes.get_or_create(target, || CritUpstreamNodeData {
|
||||
matched: false,
|
||||
tl: target.owner(),
|
||||
data: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, target: &Target) -> Option<RefMut<'_, CritUpstreamNodeData<Target, T>>> {
|
||||
self.nodes.get(target)
|
||||
}
|
||||
|
||||
pub fn has_downstream(&self) -> bool {
|
||||
self.downstream.is_not_empty()
|
||||
}
|
||||
|
||||
pub fn matched(&self, target: &Target) -> bool {
|
||||
let Some(node) = self.nodes.get(target) else {
|
||||
return false;
|
||||
};
|
||||
node.matched
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
criteria::{
|
||||
CritUpstreamNode,
|
||||
crit_graph::{CritDownstream, CritDownstreamData, CritMgr, CritTarget},
|
||||
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||
},
|
||||
utils::{cell_ext::CellExt, queue::AsyncQueue},
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
rc::{Rc, Weak},
|
||||
slice,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct CritLeafMatcher<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
upstream: CritDownstreamData<Target>,
|
||||
on_match: Box<dyn Fn(Target::LeafData) -> Box<dyn FnOnce()>>,
|
||||
targets: CritPerTargetData<Target, NodeHolder<Target>>,
|
||||
events: Rc<AsyncQueue<CritLeafEvent<Target>>>,
|
||||
}
|
||||
|
||||
pub(in crate::criteria) struct NodeHolder<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
node: Rc<Node<Target>>,
|
||||
}
|
||||
|
||||
struct Node<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
leaf: Weak<CritLeafMatcher<Target>>,
|
||||
target_id: Target::Id,
|
||||
needs_event: Cell<bool>,
|
||||
new_data: Cell<Option<Target::LeafData>>,
|
||||
data: Cell<Option<Target::LeafData>>,
|
||||
on_unmatch: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
}
|
||||
|
||||
pub struct CritLeafEvent<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
node: Rc<Node<Target>>,
|
||||
}
|
||||
|
||||
impl<Target> CritDownstream<Target> for CritLeafMatcher<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn update_matched(self: Rc<Self>, data: &Target, matched: bool) {
|
||||
let node = &self
|
||||
.targets
|
||||
.get_or_create(data, || {
|
||||
let node = Rc::new(Node {
|
||||
leaf: Rc::downgrade(&self),
|
||||
target_id: data.id(),
|
||||
needs_event: Cell::new(true),
|
||||
new_data: Cell::new(None),
|
||||
data: Cell::new(None),
|
||||
on_unmatch: Cell::new(None),
|
||||
});
|
||||
NodeHolder { node: node.clone() }
|
||||
})
|
||||
.node;
|
||||
self.push_event(node, matched.then_some(data.leaf_data()));
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritLeafMatcher<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub(in crate::criteria) fn new(
|
||||
mgr: &Target::Mgr,
|
||||
upstream: &Rc<dyn CritUpstreamNode<Target>>,
|
||||
on_match: impl Fn(Target::LeafData) -> Box<dyn FnOnce()> + 'static,
|
||||
) -> Rc<Self> {
|
||||
let id = mgr.id();
|
||||
let slf = Rc::new_cyclic(|slf| Self {
|
||||
targets: CritPerTargetData::new(slf, id),
|
||||
on_match: Box::new(on_match),
|
||||
events: mgr.leaf_events().clone(),
|
||||
upstream: CritDownstreamData::new(id, slice::from_ref(upstream)),
|
||||
});
|
||||
slf.upstream.attach(&slf);
|
||||
slf
|
||||
}
|
||||
|
||||
fn push_event(&self, node: &Rc<Node<Target>>, new_data: Option<Target::LeafData>) {
|
||||
node.new_data.set(new_data);
|
||||
if node.needs_event.replace(false) {
|
||||
self.events.push(CritLeafEvent { node: node.clone() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritLeafEvent<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub fn run(self) {
|
||||
let n = self.node;
|
||||
n.needs_event.set(true);
|
||||
if n.new_data != n.data
|
||||
&& let Some(on_unmatch) = n.on_unmatch.take()
|
||||
{
|
||||
if n.leaf.strong_count() == 0 {
|
||||
return;
|
||||
}
|
||||
on_unmatch();
|
||||
}
|
||||
n.data.set(n.new_data.get());
|
||||
if n.data.is_some() != n.on_unmatch.is_some() {
|
||||
let Some(leaf) = n.leaf.upgrade() else {
|
||||
return;
|
||||
};
|
||||
if let Some(id) = n.data.get() {
|
||||
n.on_unmatch.set(Some((leaf.on_match)(id)));
|
||||
} else {
|
||||
if let Some(on_unmatch) = n.on_unmatch.take() {
|
||||
on_unmatch();
|
||||
}
|
||||
leaf.targets.remove(n.target_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> Drop for NodeHolder<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if let Some(leaf) = self.node.leaf.upgrade() {
|
||||
leaf.push_event(&self.node, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritDestroyListenerBase<Target> for CritLeafMatcher<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Data = NodeHolder<Target>;
|
||||
|
||||
fn data(&self) -> &CritPerTargetData<Target, Self::Data> {
|
||||
&self.targets
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
pub mod critm_any_or_all;
|
||||
pub mod critm_constant;
|
||||
pub mod critm_exactly;
|
||||
pub mod critm_string;
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
use {
|
||||
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
||||
std::{marker::PhantomData, rc::Rc},
|
||||
};
|
||||
|
||||
pub struct CritMatchAnyOrAll<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
all: bool,
|
||||
total: usize,
|
||||
_phantom: PhantomData<fn(&Target)>,
|
||||
}
|
||||
|
||||
impl<Target> CritMatchAnyOrAll<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub fn new(upstream: &[Rc<dyn CritUpstreamNode<Target>>], all: bool) -> Self {
|
||||
Self {
|
||||
all,
|
||||
total: upstream.len(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritMiddleCriterion<Target> for CritMatchAnyOrAll<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Data = usize;
|
||||
type Not = Self;
|
||||
|
||||
fn update_matched(&self, _data: &Target, node: &mut usize, matched: bool) -> bool {
|
||||
if matched {
|
||||
*node += 1;
|
||||
} else {
|
||||
*node -= 1;
|
||||
}
|
||||
if self.all {
|
||||
*node == self.total
|
||||
} else {
|
||||
*node > 0
|
||||
}
|
||||
}
|
||||
|
||||
fn pull(&self, upstream: &[Rc<dyn CritUpstreamNode<Target>>], node: &Target) -> bool {
|
||||
for upstream in upstream {
|
||||
if upstream.pull(node) {
|
||||
if !self.all {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if self.all {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.all
|
||||
}
|
||||
|
||||
fn not(&self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
all: !self.all,
|
||||
total: self.total,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
use {
|
||||
crate::criteria::{
|
||||
CritMatcherIds, FixedRootMatcher,
|
||||
crit_graph::{
|
||||
CritFixedRootCriterion, CritFixedRootCriterionBase, CritMgr, CritRoot, CritRootFixed,
|
||||
CritTarget,
|
||||
},
|
||||
},
|
||||
linearize::static_map,
|
||||
std::{marker::PhantomData, rc::Rc},
|
||||
};
|
||||
|
||||
pub struct CritMatchConstant<Target>(pub bool, pub PhantomData<fn(&Target)>);
|
||||
|
||||
impl<Target> CritMatchConstant<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub fn create(
|
||||
roots: &Rc<Target::RootMatchers>,
|
||||
ids: &CritMatcherIds,
|
||||
) -> FixedRootMatcher<Target, CritMatchConstant<Target>> {
|
||||
static_map! {
|
||||
v => CritRoot::new(
|
||||
roots,
|
||||
ids.next(),
|
||||
CritRootFixed(Self(v, PhantomData), PhantomData),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritFixedRootCriterionBase<Target> for CritMatchConstant<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn constant(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn not<'a>(&self, mgr: &'a Target::Mgr) -> &'a FixedRootMatcher<Target, Self>
|
||||
where
|
||||
Self: CritFixedRootCriterion<Target>,
|
||||
{
|
||||
mgr.match_constant()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritFixedRootCriterion<Target> for CritMatchConstant<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
const COMPARE: bool = false;
|
||||
|
||||
fn matches(&self, _data: &Target) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
use {
|
||||
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
||||
std::{marker::PhantomData, rc::Rc},
|
||||
};
|
||||
|
||||
pub struct CritMatchExactly<Target> {
|
||||
total: usize,
|
||||
num: usize,
|
||||
not: bool,
|
||||
_phantom: PhantomData<fn(&Target)>,
|
||||
}
|
||||
|
||||
impl<Target> CritMatchExactly<Target> {
|
||||
pub fn new(upstream: &[Rc<dyn CritUpstreamNode<Target>>], num: usize) -> Self {
|
||||
Self {
|
||||
total: upstream.len(),
|
||||
num,
|
||||
not: false,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target> CritMiddleCriterion<Target> for CritMatchExactly<Target>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Data = usize;
|
||||
type Not = Self;
|
||||
|
||||
fn update_matched(&self, _data: &Target, node: &mut usize, matched: bool) -> bool {
|
||||
if matched {
|
||||
*node += 1;
|
||||
} else {
|
||||
*node -= 1;
|
||||
}
|
||||
(*node == self.num) ^ self.not
|
||||
}
|
||||
|
||||
fn pull(&self, upstream: &[Rc<dyn CritUpstreamNode<Target>>], node: &Target) -> bool {
|
||||
let mut n = 0;
|
||||
for upstream in upstream {
|
||||
if upstream.pull(node) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
(n == self.num) ^ self.not
|
||||
}
|
||||
|
||||
fn not(&self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
total: self.total,
|
||||
num: self.total - self.num,
|
||||
not: !self.not,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
use {
|
||||
crate::criteria::{
|
||||
CritLiteralOrRegex, RootMatcherMap,
|
||||
crit_graph::{CritRootCriterion, CritTarget},
|
||||
},
|
||||
std::marker::PhantomData,
|
||||
};
|
||||
|
||||
pub struct CritMatchString<Target, A> {
|
||||
string: CritLiteralOrRegex,
|
||||
_phantom: PhantomData<(fn(&Target), A)>,
|
||||
}
|
||||
|
||||
pub trait StringAccess<Target>: Sized + 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn with_string(data: &Target, f: impl FnOnce(&str) -> bool) -> bool;
|
||||
fn nodes(
|
||||
roots: &Target::RootMatchers,
|
||||
) -> &RootMatcherMap<Target, CritMatchString<Target, Self>>;
|
||||
}
|
||||
|
||||
impl<Target, A> CritMatchString<Target, A> {
|
||||
pub fn new(string: CritLiteralOrRegex) -> Self {
|
||||
Self {
|
||||
string,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, A> CritRootCriterion<Target> for CritMatchString<Target, A>
|
||||
where
|
||||
Target: CritTarget,
|
||||
A: StringAccess<Target>,
|
||||
{
|
||||
fn matches(&self, data: &Target) -> bool {
|
||||
A::with_string(data, |s| self.string.matches(s))
|
||||
}
|
||||
|
||||
fn nodes(roots: &Target::RootMatchers) -> Option<&RootMatcherMap<Target, Self>> {
|
||||
Some(A::nodes(roots))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
use {
|
||||
crate::criteria::{
|
||||
CritMatcherId,
|
||||
crit_graph::{CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
std::{
|
||||
cell::{RefCell, RefMut},
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Weak,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct CritPerTargetData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
id: CritMatcherId,
|
||||
slf: Weak<dyn CritDestroyListener<Target>>,
|
||||
data: RefCell<AHashMap<Target::Id, PerTreeNodeData<Target, T>>>,
|
||||
}
|
||||
|
||||
pub struct PerTreeNodeData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
node: Target::Owner,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub(super) trait CritDestroyListenerBase<Target>: 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Data;
|
||||
|
||||
fn data(&self) -> &CritPerTargetData<Target, Self::Data>;
|
||||
}
|
||||
|
||||
pub trait CritDestroyListener<Target>: 'static
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn destroyed(&self, target_id: Target::Id);
|
||||
}
|
||||
|
||||
impl<Target, T> CritPerTargetData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
pub fn new(slf: &Weak<impl CritDestroyListener<Target>>, id: CritMatcherId) -> Self {
|
||||
Self {
|
||||
id,
|
||||
slf: slf.clone() as _,
|
||||
data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.data.borrow_mut().clear();
|
||||
}
|
||||
|
||||
pub fn get_or_create(&self, target: &Target, default: impl FnOnce() -> T) -> RefMut<'_, T> {
|
||||
RefMut::map(self.data.borrow_mut(), |d| {
|
||||
&mut d
|
||||
.entry(target.id())
|
||||
.or_insert_with(|| {
|
||||
target.destroyed().set(self.id, self.slf.clone());
|
||||
PerTreeNodeData {
|
||||
node: target.owner(),
|
||||
data: default(),
|
||||
}
|
||||
})
|
||||
.data
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, target: &Target) -> Option<RefMut<'_, T>> {
|
||||
RefMut::filter_map(self.data.borrow_mut(), |d| {
|
||||
d.get_mut(&target.id()).map(|d| &mut d.data)
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn remove(&self, target_id: Target::Id) {
|
||||
if let Some(node) = self.data.borrow_mut().remove(&target_id)
|
||||
&& let Some(node) = node.node.upgrade()
|
||||
{
|
||||
node.data().destroyed().remove(&self.id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> RefMut<'_, AHashMap<Target::Id, PerTreeNodeData<Target, T>>> {
|
||||
self.data.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> Drop for CritPerTargetData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
for d in self.data.borrow().values() {
|
||||
if let Some(n) = d.node.upgrade() {
|
||||
n.data().destroyed().remove(&self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> CritDestroyListener<Target> for T
|
||||
where
|
||||
Target: CritTarget,
|
||||
T: CritDestroyListenerBase<Target>,
|
||||
{
|
||||
fn destroyed(&self, target_id: Target::Id) {
|
||||
let _v = self.data().data.borrow_mut().remove(&target_id);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> Deref for PerTreeNodeData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Target, T> DerefMut for PerTreeNodeData<Target, T>
|
||||
where
|
||||
Target: CritTarget,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
|
@ -408,10 +408,10 @@ impl CritTarget for ToplevelData {
|
|||
type Mgr = TlMatcherManager;
|
||||
type RootMatchers = RootMatchers;
|
||||
type LeafData = ToplevelIdentifier;
|
||||
type Owner = Weak<dyn ToplevelNode>;
|
||||
type Owner = WeakToplevelTargetOwner;
|
||||
|
||||
fn owner(&self) -> Self::Owner {
|
||||
self.slf.clone()
|
||||
WeakToplevelTargetOwner(self.slf.clone())
|
||||
}
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
|
|
@ -427,20 +427,24 @@ impl CritTarget for ToplevelData {
|
|||
}
|
||||
}
|
||||
|
||||
impl CritTargetOwner for Rc<dyn ToplevelNode> {
|
||||
pub struct ToplevelTargetOwner(Rc<dyn ToplevelNode>);
|
||||
|
||||
pub struct WeakToplevelTargetOwner(Weak<dyn ToplevelNode>);
|
||||
|
||||
impl CritTargetOwner for ToplevelTargetOwner {
|
||||
type Target = ToplevelData;
|
||||
|
||||
fn data(&self) -> &Self::Target {
|
||||
self.tl_data()
|
||||
self.0.tl_data()
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakCritTargetOwner for Weak<dyn ToplevelNode> {
|
||||
impl WeakCritTargetOwner for WeakToplevelTargetOwner {
|
||||
type Target = ToplevelData;
|
||||
type Owner = Rc<dyn ToplevelNode>;
|
||||
type Owner = ToplevelTargetOwner;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Owner> {
|
||||
self.upgrade()
|
||||
self.0.upgrade().map(ToplevelTargetOwner)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub struct TlmMatchClient {
|
|||
id: CritMatcherId,
|
||||
state: Rc<State>,
|
||||
node: Rc<ClmUpstreamNode>,
|
||||
upstream: CritDownstreamData<Rc<Client>>,
|
||||
upstream: CritDownstreamData<Client>,
|
||||
downstream: CritUpstreamData<ToplevelData, ()>,
|
||||
}
|
||||
|
||||
|
|
@ -73,8 +73,8 @@ impl CritUpstreamNodeBase<ToplevelData> for TlmMatchClient {
|
|||
}
|
||||
}
|
||||
|
||||
impl CritDownstream<Rc<Client>> for TlmMatchClient {
|
||||
fn update_matched(self: Rc<Self>, target: &Rc<Client>, matched: bool) {
|
||||
impl CritDownstream<Client> for TlmMatchClient {
|
||||
fn update_matched(self: Rc<Self>, target: &Client, matched: bool) {
|
||||
let handle = |data: &ToplevelData| {
|
||||
let node = match matched {
|
||||
true => self.downstream.get_or_create(data),
|
||||
|
|
|
|||
115
src/damage.rs
115
src/damage.rs
|
|
@ -2,14 +2,12 @@ use {
|
|||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
cmm::{cmm_manager::ColorManager, cmm_render_intent::RenderIntent},
|
||||
fixed::Fixed,
|
||||
ifs::wl_output::WlOutputGlobal,
|
||||
rect::{Rect, Region},
|
||||
renderer::renderer_base::RendererBase,
|
||||
state::State,
|
||||
theme::Color,
|
||||
time::Time,
|
||||
tree::Transform,
|
||||
utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd},
|
||||
},
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
|
|
@ -22,6 +20,8 @@ use {
|
|||
uapi::c::CLOCK_MONOTONIC,
|
||||
};
|
||||
|
||||
pub use jay_damage::*;
|
||||
|
||||
pub async fn visualize_damage(state: Rc<State>) {
|
||||
let timer = match TimerFd::new(CLOCK_MONOTONIC) {
|
||||
Ok(t) => t,
|
||||
|
|
@ -196,114 +196,3 @@ impl DamageVisualizer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DamageMatrix {
|
||||
transform: Transform,
|
||||
mx: f64,
|
||||
my: f64,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
smear: i32,
|
||||
}
|
||||
|
||||
impl Default for DamageMatrix {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transform: Default::default(),
|
||||
mx: 1.0,
|
||||
my: 1.0,
|
||||
dx: 0.0,
|
||||
dy: 0.0,
|
||||
smear: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DamageMatrix {
|
||||
pub fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
|
||||
let x1 = rect.x1() - self.smear;
|
||||
let x2 = rect.x2() + self.smear;
|
||||
let y1 = rect.y1() - self.smear;
|
||||
let y2 = rect.y2() + self.smear;
|
||||
let [x1, y1, x2, y2] = match self.transform {
|
||||
Transform::None => [x1, y1, x2, y2],
|
||||
Transform::Rotate90 => [-y2, x1, -y1, x2],
|
||||
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
|
||||
Transform::Rotate270 => [y1, -x2, y2, -x1],
|
||||
Transform::Flip => [-x2, y1, -x1, y2],
|
||||
Transform::FlipRotate90 => [y1, x1, y2, x2],
|
||||
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
|
||||
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
|
||||
};
|
||||
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
|
||||
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
|
||||
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
|
||||
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
|
||||
Rect::new_saturating(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
transform: Transform,
|
||||
legacy_scale: i32,
|
||||
buffer_width: i32,
|
||||
buffer_height: i32,
|
||||
viewport: Option<[Fixed; 4]>,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> DamageMatrix {
|
||||
let mut buffer_width = buffer_width as f64;
|
||||
let mut buffer_height = buffer_height as f64;
|
||||
let dst_width = dst_width as f64;
|
||||
let dst_height = dst_height as f64;
|
||||
|
||||
let mut mx = 1.0;
|
||||
let mut my = 1.0;
|
||||
if legacy_scale != 1 {
|
||||
let scale_inv = 1.0 / (legacy_scale as f64);
|
||||
mx = scale_inv;
|
||||
my = scale_inv;
|
||||
buffer_width *= scale_inv;
|
||||
buffer_height *= scale_inv;
|
||||
}
|
||||
let (mut buffer_width, mut buffer_height) =
|
||||
transform.maybe_swap((buffer_width, buffer_height));
|
||||
let (mut dx, mut dy) = match transform {
|
||||
Transform::None => (0.0, 0.0),
|
||||
Transform::Rotate90 => (buffer_width, 0.0),
|
||||
Transform::Rotate180 => (buffer_width, buffer_height),
|
||||
Transform::Rotate270 => (0.0, buffer_height),
|
||||
Transform::Flip => (buffer_width, 0.0),
|
||||
Transform::FlipRotate90 => (0.0, 0.0),
|
||||
Transform::FlipRotate180 => (0.0, buffer_height),
|
||||
Transform::FlipRotate270 => (buffer_width, buffer_height),
|
||||
};
|
||||
if let Some([x, y, w, h]) = viewport {
|
||||
dx -= x.to_f64();
|
||||
dy -= y.to_f64();
|
||||
buffer_width = w.to_f64();
|
||||
buffer_height = h.to_f64();
|
||||
}
|
||||
let mut smear = false;
|
||||
if dst_width != buffer_width {
|
||||
let scale = dst_width / buffer_width;
|
||||
mx *= scale;
|
||||
dx *= scale;
|
||||
smear |= dst_width > buffer_width;
|
||||
}
|
||||
if dst_height != buffer_height {
|
||||
let scale = dst_height / buffer_height;
|
||||
my *= scale;
|
||||
dy *= scale;
|
||||
smear |= dst_height > buffer_height;
|
||||
}
|
||||
DamageMatrix {
|
||||
transform,
|
||||
mx,
|
||||
my,
|
||||
dx,
|
||||
dy,
|
||||
smear: smear as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
535
src/dbus.rs
535
src/dbus.rs
|
|
@ -1,145 +1,39 @@
|
|||
pub use types::*;
|
||||
pub use jay_dbus_core::*;
|
||||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
dbus::{
|
||||
property::{Get, GetReply},
|
||||
types::{ObjectPath, Signature, Variant},
|
||||
},
|
||||
io_uring::{IoUring, IoUringError},
|
||||
io_uring::IoUring,
|
||||
utils::{
|
||||
buf::DynamicBuf,
|
||||
bufio::{BufIo, BufIoError},
|
||||
bufio::BufIo,
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
numcell::NumCell,
|
||||
oserror::OsError,
|
||||
run_toplevel::RunToplevel,
|
||||
stack::Stack,
|
||||
vecstorage::VecStorage,
|
||||
xrd::{XRD, xrd},
|
||||
},
|
||||
wire_dbus::{
|
||||
org,
|
||||
org::freedesktop::dbus::properties::{GetAll, GetAllReply, PropertiesChanged},
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
std::{
|
||||
borrow::{Borrow, Cow},
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Display},
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
task::{Context, Poll, Waker},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
mod auth;
|
||||
mod dynamic_type;
|
||||
mod formatter;
|
||||
mod holder;
|
||||
mod incoming;
|
||||
mod outgoing;
|
||||
mod parser;
|
||||
mod property;
|
||||
mod socket;
|
||||
mod types;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CallError {
|
||||
pub name: String,
|
||||
pub msg: Option<String>,
|
||||
}
|
||||
|
||||
impl Display for CallError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(msg) = &self.msg {
|
||||
write!(f, "{}: {}", self.name, msg)
|
||||
} else {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DbusError {
|
||||
#[error("Encountered an unknown type in a signature")]
|
||||
UnknownType,
|
||||
#[error("Function call reply does not contain a reply serial")]
|
||||
NoReplySerial,
|
||||
#[error("Signal message contains no interface or member or path")]
|
||||
MissingSignalHeaders,
|
||||
#[error("Method call message contains no interface or member or path")]
|
||||
MissingMethodCallHeaders,
|
||||
#[error("Error has no error name")]
|
||||
NoErrorName,
|
||||
#[error("The socket was killed")]
|
||||
Killed,
|
||||
#[error("{0}")]
|
||||
CallError(CallError),
|
||||
#[error("FD index is out of bounds")]
|
||||
OobFds,
|
||||
#[error("Variant has an invalid type")]
|
||||
InvalidVariantType,
|
||||
#[error("Could not create a socket")]
|
||||
Socket(#[source] OsError),
|
||||
#[error("Could not connect")]
|
||||
Connect(#[source] IoUringError),
|
||||
#[error("Could not write to the dbus socket")]
|
||||
WriteError(#[source] IoUringError),
|
||||
#[error("Could not read from the dbus socket")]
|
||||
ReadError(#[source] IoUringError),
|
||||
#[error("timeout")]
|
||||
IoUringError(#[source] Box<IoUringError>),
|
||||
#[error("Server did not send auth challenge")]
|
||||
NoChallenge,
|
||||
#[error("Server did not accept our authentication")]
|
||||
Auth,
|
||||
#[error("Array length is not a multiple of the element size")]
|
||||
PodArrayLength,
|
||||
#[error("Peer did not send enough fds")]
|
||||
TooFewFds,
|
||||
#[error("Variant signature is not a single type")]
|
||||
TrailingVariantSignature,
|
||||
#[error("Dict signature does not contain a terminating '}}'")]
|
||||
UnterminatedDict,
|
||||
#[error("Struct signature does not contain a terminating '}}'")]
|
||||
UnterminatedStruct,
|
||||
#[error("Dict signature contains trailing types")]
|
||||
DictTrailing,
|
||||
#[error("String does not contain valid UTF-8")]
|
||||
InvalidUtf8,
|
||||
#[error("Unexpected end of message")]
|
||||
UnexpectedEof,
|
||||
#[error("Boolean value was not 0 or 1")]
|
||||
InvalidBoolValue,
|
||||
#[error("Signature is empty")]
|
||||
EmptySignature,
|
||||
#[error("The session bus address is not set")]
|
||||
SessionBusAddressNotSet,
|
||||
#[error("Server does not support FD passing")]
|
||||
UnixFd,
|
||||
#[error("Server message has a different endianess than ourselves")]
|
||||
InvalidEndianess,
|
||||
#[error("Server speaks an unexpected protocol version")]
|
||||
InvalidProtocol,
|
||||
#[error("Signature contains an invalid type")]
|
||||
InvalidSignatureType,
|
||||
#[error("The signal already has a handler")]
|
||||
AlreadyHandled,
|
||||
#[error(transparent)]
|
||||
BufIoError(#[from] BufIoError),
|
||||
#[error(transparent)]
|
||||
DbusError(Rc<DbusError>),
|
||||
}
|
||||
efrom!(DbusError, IoUringError);
|
||||
|
||||
const DBUS_SESSION_BUS_ADDRESS: &str = "DBUS_SESSION_BUS_ADDRESS";
|
||||
|
||||
|
|
@ -280,42 +174,8 @@ pub struct DbusSocket {
|
|||
headers: RefCell<VecStorage<(u8, Variant<'static>)>>,
|
||||
run_toplevel: Rc<RunToplevel>,
|
||||
signal_handlers: RefCell<AHashMap<(&'static str, &'static str), InterfaceSignalHandlers>>,
|
||||
objects: CopyHashMap<Cow<'static, str>, Rc<DbusObjectData>>,
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct MemberHandlerOwnedKey {
|
||||
key: MemberHandlerKey<'static>,
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct MemberHandlerKey<'a> {
|
||||
interface: &'a str,
|
||||
member: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Borrow<MemberHandlerKey<'a>> for MemberHandlerOwnedKey {
|
||||
fn borrow(&self) -> &MemberHandlerKey<'a> {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
const TY_BYTE: u8 = b'y';
|
||||
const TY_BOOLEAN: u8 = b'b';
|
||||
const TY_INT16: u8 = b'n';
|
||||
const TY_UINT16: u8 = b'q';
|
||||
const TY_INT32: u8 = b'i';
|
||||
const TY_UINT32: u8 = b'u';
|
||||
const TY_INT64: u8 = b'x';
|
||||
const TY_UINT64: u8 = b't';
|
||||
const TY_DOUBLE: u8 = b'd';
|
||||
const TY_STRING: u8 = b's';
|
||||
const TY_OBJECT_PATH: u8 = b'o';
|
||||
const TY_SIGNATURE: u8 = b'g';
|
||||
const TY_ARRAY: u8 = b'a';
|
||||
const TY_VARIANT: u8 = b'v';
|
||||
const TY_UNIX_FD: u8 = b'h';
|
||||
|
||||
const HDR_PATH: u8 = 1;
|
||||
const HDR_INTERFACE: u8 = 2;
|
||||
const HDR_MEMBER: u8 = 3;
|
||||
|
|
@ -337,20 +197,6 @@ const NO_AUTO_START: u8 = 0x2;
|
|||
#[expect(dead_code)]
|
||||
const ALLOW_INTERACTIVE_AUTHORIZATION: u8 = 0x4;
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub const DBUS_NAME_FLAG_ALLOW_REPLACEMENT: u32 = 0x1;
|
||||
#[expect(dead_code)]
|
||||
pub const DBUS_NAME_FLAG_REPLACE_EXISTING: u32 = 0x2;
|
||||
pub const DBUS_NAME_FLAG_DO_NOT_QUEUE: u32 = 0x4;
|
||||
|
||||
pub const DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: u32 = 1;
|
||||
#[expect(dead_code)]
|
||||
pub const DBUS_REQUEST_NAME_REPLY_IN_QUEUE: u32 = 2;
|
||||
#[expect(dead_code)]
|
||||
pub const DBUS_REQUEST_NAME_REPLY_EXISTS: u32 = 3;
|
||||
#[expect(dead_code)]
|
||||
pub const DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: u32 = 4;
|
||||
|
||||
pub const BUS_DEST: &str = "org.freedesktop.DBus";
|
||||
pub const BUS_PATH: &str = "/org/freedesktop/DBus";
|
||||
|
||||
|
|
@ -397,102 +243,6 @@ impl Drop for DbusHolder {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DynamicType {
|
||||
U8,
|
||||
Bool,
|
||||
I16,
|
||||
U16,
|
||||
I32,
|
||||
U32,
|
||||
I64,
|
||||
U64,
|
||||
F64,
|
||||
String,
|
||||
ObjectPath,
|
||||
Signature,
|
||||
Variant,
|
||||
Fd,
|
||||
Array(Box<DynamicType>),
|
||||
DictEntry(Box<DynamicType>, Box<DynamicType>),
|
||||
Struct(Vec<DynamicType>),
|
||||
}
|
||||
|
||||
pub struct Parser<'a> {
|
||||
buf: &'a [u8],
|
||||
pos: usize,
|
||||
fds: &'a [Rc<OwnedFd>],
|
||||
}
|
||||
|
||||
pub struct Formatter<'a> {
|
||||
fds: &'a mut Vec<Rc<OwnedFd>>,
|
||||
buf: &'a mut DynamicBuf,
|
||||
}
|
||||
|
||||
pub unsafe trait Message<'a>: Sized + 'a {
|
||||
const SIGNATURE: &'static str;
|
||||
const INTERFACE: &'static str;
|
||||
const MEMBER: &'static str;
|
||||
type Generic<'b>: Message<'b>;
|
||||
|
||||
fn marshal(&self, w: &mut Formatter);
|
||||
fn unmarshal(p: &mut Parser<'a>) -> Result<Self, DbusError>;
|
||||
fn num_fds(&self) -> u32;
|
||||
}
|
||||
|
||||
pub struct ErrorMessage<'a> {
|
||||
pub msg: Cow<'a, str>,
|
||||
}
|
||||
|
||||
unsafe impl<'a> Message<'a> for ErrorMessage<'a> {
|
||||
const SIGNATURE: &'static str = "s";
|
||||
const INTERFACE: &'static str = "";
|
||||
const MEMBER: &'static str = "";
|
||||
type Generic<'b> = ErrorMessage<'b>;
|
||||
|
||||
fn marshal(&self, w: &mut Formatter) {
|
||||
self.msg.marshal(w)
|
||||
}
|
||||
|
||||
fn unmarshal(p: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
Ok(Self {
|
||||
msg: p.unmarshal()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Property {
|
||||
const INTERFACE: &'static str;
|
||||
const PROPERTY: &'static str;
|
||||
type Type: DbusType<'static>;
|
||||
}
|
||||
|
||||
pub trait Signal<'a>: Message<'a> {}
|
||||
|
||||
pub trait MethodCall<'a>: Message<'a> {
|
||||
type Reply: Message<'static>;
|
||||
}
|
||||
|
||||
pub unsafe trait DbusType<'a>: Clone + 'a {
|
||||
const ALIGNMENT: usize;
|
||||
const IS_POD: bool;
|
||||
type Generic<'b>: DbusType<'b> + 'b;
|
||||
|
||||
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError>;
|
||||
#[allow(clippy::allow_attributes, dead_code)]
|
||||
fn write_signature(w: &mut Vec<u8>);
|
||||
fn marshal(&self, fmt: &mut Formatter);
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError>;
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reply<T: Message<'static>> {
|
||||
socket: Rc<DbusSocket>,
|
||||
buf: Vec<u8>,
|
||||
|
|
@ -649,278 +399,3 @@ struct InterfaceSignalHandlers {
|
|||
unconditional: Option<Rc<dyn SignalHandlerApi>>,
|
||||
conditional: AHashMap<String, Rc<dyn SignalHandlerApi>>,
|
||||
}
|
||||
|
||||
struct DbusObjectData {
|
||||
path: Cow<'static, str>,
|
||||
methods: CopyHashMap<MemberHandlerOwnedKey, Rc<dyn MethodHandlerApi>>,
|
||||
properties: CopyHashMap<MemberHandlerOwnedKey, Rc<dyn PropertyHandlerApi>>,
|
||||
}
|
||||
|
||||
pub struct DbusObject {
|
||||
socket: Rc<DbusSocket>,
|
||||
data: Rc<DbusObjectData>,
|
||||
}
|
||||
|
||||
impl Drop for DbusObject {
|
||||
fn drop(&mut self) {
|
||||
self.socket.objects.remove(&self.data.path);
|
||||
}
|
||||
}
|
||||
|
||||
impl DbusObject {
|
||||
pub fn add_method<T, F>(&self, handler: F)
|
||||
where
|
||||
T: MethodCall<'static>,
|
||||
F: for<'a> Fn(T::Generic<'a>, PendingReply<T::Reply>) + 'static,
|
||||
{
|
||||
let rhd = Rc::new(MethodHandlerData {
|
||||
handler,
|
||||
_phantom: Default::default(),
|
||||
});
|
||||
let key = MemberHandlerOwnedKey {
|
||||
key: MemberHandlerKey {
|
||||
interface: T::INTERFACE,
|
||||
member: T::MEMBER,
|
||||
},
|
||||
};
|
||||
self.data.methods.set(key, rhd);
|
||||
}
|
||||
|
||||
pub fn set_property<T>(&self, value: Variant<'static>)
|
||||
where
|
||||
T: Property + 'static,
|
||||
{
|
||||
self.emit_signal(&PropertiesChanged {
|
||||
interface_name: T::INTERFACE.into(),
|
||||
changed_properties: Cow::Borrowed(&[DictEntry {
|
||||
key: T::PROPERTY.into(),
|
||||
value: value.borrow(),
|
||||
}]),
|
||||
invalidated_properties: Default::default(),
|
||||
});
|
||||
let phd = Rc::new(PropertyHandlerData::<T> {
|
||||
data: value,
|
||||
_phantom: Default::default(),
|
||||
});
|
||||
let key = MemberHandlerOwnedKey {
|
||||
key: MemberHandlerKey {
|
||||
interface: T::INTERFACE,
|
||||
member: T::PROPERTY,
|
||||
},
|
||||
};
|
||||
self.data.properties.set(key, phd);
|
||||
}
|
||||
|
||||
pub fn emit_signal<'a, T: Signal<'a>>(&self, signal: &T) {
|
||||
self.socket.emit_signal(&self.data.path, signal);
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &str {
|
||||
&self.data.path
|
||||
}
|
||||
}
|
||||
|
||||
trait PropertyHandlerApi {
|
||||
fn interface(&self) -> &'static str;
|
||||
fn member(&self) -> &'static str;
|
||||
fn value<'a>(&'a self) -> Variant<'a>;
|
||||
}
|
||||
|
||||
struct PropertyHandlerData<T> {
|
||||
data: Variant<'static>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> PropertyHandlerApi for PropertyHandlerData<T>
|
||||
where
|
||||
T: Property,
|
||||
{
|
||||
fn interface(&self) -> &'static str {
|
||||
T::INTERFACE
|
||||
}
|
||||
|
||||
fn member(&self) -> &'static str {
|
||||
T::PROPERTY
|
||||
}
|
||||
|
||||
fn value<'a>(&'a self) -> Variant<'a> {
|
||||
self.data.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PendingReply<T> {
|
||||
reply_expected: bool,
|
||||
socket: Rc<DbusSocket>,
|
||||
destination: String,
|
||||
serial: u32,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> PendingReply<T> {
|
||||
#[expect(dead_code)]
|
||||
pub fn reply_expected(&self) -> bool {
|
||||
self.reply_expected
|
||||
}
|
||||
|
||||
pub fn err(&self, msg: &str) {
|
||||
if self.reply_expected {
|
||||
self.socket.send_error(&self.destination, self.serial, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PendingReply<T>
|
||||
where
|
||||
T: Message<'static>,
|
||||
{
|
||||
pub fn ok<'a>(&self, msg: &T::Generic<'a>) {
|
||||
if self.reply_expected {
|
||||
self.socket.send_reply(&self.destination, self.serial, msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn complete<'a>(&self, res: Result<&T::Generic<'a>, &str>) {
|
||||
match res {
|
||||
Ok(m) => self.ok(m),
|
||||
Err(e) => self.err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait MethodHandlerApi {
|
||||
fn signature(&self) -> &'static str;
|
||||
fn handle(
|
||||
&self,
|
||||
object: &DbusObjectData,
|
||||
socket: &Rc<DbusSocket>,
|
||||
dest: &str,
|
||||
serial: u32,
|
||||
reply_expected: bool,
|
||||
parser: &mut Parser,
|
||||
) -> Result<(), DbusError>;
|
||||
}
|
||||
|
||||
struct MethodHandlerData<T, F> {
|
||||
handler: F,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, F> MethodHandlerApi for MethodHandlerData<T, F>
|
||||
where
|
||||
T: MethodCall<'static>,
|
||||
F: for<'a> Fn(T::Generic<'a>, PendingReply<T::Reply>) + 'static,
|
||||
{
|
||||
fn signature(&self) -> &'static str {
|
||||
T::SIGNATURE
|
||||
}
|
||||
|
||||
fn handle<'a>(
|
||||
&self,
|
||||
_object: &DbusObjectData,
|
||||
socket: &Rc<DbusSocket>,
|
||||
dest: &str,
|
||||
serial: u32,
|
||||
reply_expected: bool,
|
||||
parser: &mut Parser<'a>,
|
||||
) -> Result<(), DbusError> {
|
||||
let msg = T::Generic::<'a>::unmarshal(parser)?;
|
||||
let pr = PendingReply {
|
||||
reply_expected,
|
||||
socket: socket.clone(),
|
||||
destination: dest.to_string(),
|
||||
serial,
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
(self.handler)(msg, pr);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct PropertyGetHandlerProxy;
|
||||
|
||||
impl MethodHandlerApi for PropertyGetHandlerProxy {
|
||||
fn signature(&self) -> &'static str {
|
||||
Get::<u32>::SIGNATURE
|
||||
}
|
||||
|
||||
fn handle<'a>(
|
||||
&self,
|
||||
object: &DbusObjectData,
|
||||
socket: &Rc<DbusSocket>,
|
||||
dest: &str,
|
||||
serial: u32,
|
||||
reply_expected: bool,
|
||||
parser: &mut Parser<'a>,
|
||||
) -> Result<(), DbusError> {
|
||||
if !reply_expected {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = org::freedesktop::dbus::properties::Get::unmarshal(parser)?;
|
||||
let key = MemberHandlerKey {
|
||||
interface: msg.interface_name.deref(),
|
||||
member: msg.property_name.deref(),
|
||||
};
|
||||
match object.properties.get(&key) {
|
||||
Some(h) => socket.send_reply(
|
||||
dest,
|
||||
serial,
|
||||
&org::freedesktop::dbus::properties::GetReply { value: h.value() },
|
||||
),
|
||||
_ => socket.send_error(dest, serial, "Property does not exist"),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct PropertyGetAllHandlerProxy;
|
||||
|
||||
impl MethodHandlerApi for PropertyGetAllHandlerProxy {
|
||||
fn signature(&self) -> &'static str {
|
||||
GetAll::SIGNATURE
|
||||
}
|
||||
|
||||
fn handle<'a>(
|
||||
&self,
|
||||
object: &DbusObjectData,
|
||||
socket: &Rc<DbusSocket>,
|
||||
dest: &str,
|
||||
serial: u32,
|
||||
reply_expected: bool,
|
||||
parser: &mut Parser<'a>,
|
||||
) -> Result<(), DbusError> {
|
||||
if !reply_expected {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = GetAll::unmarshal(parser)?;
|
||||
let all_props = object.properties.lock();
|
||||
let mut props = vec![];
|
||||
for property in all_props.values() {
|
||||
if property.interface() == msg.interface_name {
|
||||
props.push(DictEntry {
|
||||
key: property.member().into(),
|
||||
value: property.value(),
|
||||
});
|
||||
}
|
||||
}
|
||||
socket.send_reply(
|
||||
dest,
|
||||
serial,
|
||||
&GetAllReply {
|
||||
props: props.into(),
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use {
|
||||
super::{
|
||||
DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, Signal,
|
||||
types::{Bool, DictEntry, ObjectPath, Variant},
|
||||
},
|
||||
std::{borrow::Cow, rc::Rc},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,186 +0,0 @@
|
|||
use {
|
||||
super::{
|
||||
TY_ARRAY, TY_BOOLEAN, TY_BYTE, TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH,
|
||||
TY_SIGNATURE, TY_STRING, TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
|
||||
},
|
||||
crate::{
|
||||
dbus::{DbusError, DynamicType, Parser, types::Variant},
|
||||
utils::buf::DynamicBuf,
|
||||
},
|
||||
std::ops::Deref,
|
||||
};
|
||||
|
||||
impl DynamicType {
|
||||
pub fn from_signature<'a>(mut s: &'a [u8]) -> Result<(DynamicType, &'a [u8]), DbusError> {
|
||||
if s.is_empty() {
|
||||
return Err(DbusError::EmptySignature);
|
||||
}
|
||||
let first = s[0];
|
||||
s = &s[1..];
|
||||
let dp = match first {
|
||||
TY_BYTE => DynamicType::U8,
|
||||
TY_BOOLEAN => DynamicType::Bool,
|
||||
TY_INT16 => DynamicType::I16,
|
||||
TY_UINT16 => DynamicType::U16,
|
||||
TY_INT32 => DynamicType::I32,
|
||||
TY_UINT32 => DynamicType::U32,
|
||||
TY_INT64 => DynamicType::I64,
|
||||
TY_UINT64 => DynamicType::U64,
|
||||
TY_DOUBLE => DynamicType::F64,
|
||||
TY_STRING => DynamicType::String,
|
||||
TY_OBJECT_PATH => DynamicType::ObjectPath,
|
||||
TY_SIGNATURE => DynamicType::Signature,
|
||||
TY_VARIANT => DynamicType::Variant,
|
||||
TY_UNIX_FD => DynamicType::Fd,
|
||||
TY_ARRAY => {
|
||||
let (elty, rem) = Self::from_signature(s)?;
|
||||
s = rem;
|
||||
DynamicType::Array(Box::new(elty))
|
||||
}
|
||||
b'{' => {
|
||||
let (keyty, rem) = Self::from_signature(s)?;
|
||||
let (valty, rem) = Self::from_signature(rem)?;
|
||||
if rem.is_empty() {
|
||||
return Err(DbusError::UnterminatedDict);
|
||||
}
|
||||
if rem[0] != b'}' {
|
||||
return Err(DbusError::DictTrailing);
|
||||
}
|
||||
s = &rem[1..];
|
||||
DynamicType::DictEntry(Box::new(keyty), Box::new(valty))
|
||||
}
|
||||
b'(' => {
|
||||
let mut fields = vec![];
|
||||
loop {
|
||||
if s.is_empty() {
|
||||
return Err(DbusError::UnterminatedStruct);
|
||||
}
|
||||
if s[0] == b')' {
|
||||
s = &s[1..];
|
||||
break DynamicType::Struct(fields);
|
||||
}
|
||||
let (fieldty, rem) = Self::from_signature(s)?;
|
||||
s = rem;
|
||||
fields.push(fieldty);
|
||||
}
|
||||
}
|
||||
_ => return Err(DbusError::UnknownType),
|
||||
};
|
||||
Ok((dp, s))
|
||||
}
|
||||
|
||||
pub fn alignment(&self) -> usize {
|
||||
match self {
|
||||
DynamicType::U8 => 1,
|
||||
DynamicType::Bool => 4,
|
||||
DynamicType::I16 => 2,
|
||||
DynamicType::U16 => 2,
|
||||
DynamicType::I32 => 4,
|
||||
DynamicType::U32 => 4,
|
||||
DynamicType::I64 => 8,
|
||||
DynamicType::U64 => 8,
|
||||
DynamicType::F64 => 8,
|
||||
DynamicType::String => 4,
|
||||
DynamicType::ObjectPath => 4,
|
||||
DynamicType::Signature => 1,
|
||||
DynamicType::Variant => 1,
|
||||
DynamicType::Array(_) => 4,
|
||||
DynamicType::DictEntry(_, _) => 8,
|
||||
DynamicType::Struct(_) => 8,
|
||||
DynamicType::Fd => 4,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_signature(&self, w: &mut DynamicBuf) {
|
||||
let c = match self {
|
||||
DynamicType::U8 => TY_BYTE,
|
||||
DynamicType::Bool => TY_BOOLEAN,
|
||||
DynamicType::I16 => TY_INT16,
|
||||
DynamicType::U16 => TY_UINT16,
|
||||
DynamicType::I32 => TY_INT32,
|
||||
DynamicType::U32 => TY_UINT32,
|
||||
DynamicType::I64 => TY_INT64,
|
||||
DynamicType::U64 => TY_UINT64,
|
||||
DynamicType::F64 => TY_DOUBLE,
|
||||
DynamicType::String => TY_STRING,
|
||||
DynamicType::ObjectPath => TY_OBJECT_PATH,
|
||||
DynamicType::Signature => TY_SIGNATURE,
|
||||
DynamicType::Variant => TY_VARIANT,
|
||||
DynamicType::Fd => TY_UNIX_FD,
|
||||
DynamicType::Array(el) => {
|
||||
w.push(TY_ARRAY);
|
||||
el.write_signature(w);
|
||||
return;
|
||||
}
|
||||
DynamicType::DictEntry(k, v) => {
|
||||
w.push(b'{');
|
||||
k.write_signature(w);
|
||||
v.write_signature(w);
|
||||
w.push(b'}');
|
||||
return;
|
||||
}
|
||||
DynamicType::Struct(f) => {
|
||||
w.push(b'(');
|
||||
for f in f {
|
||||
f.write_signature(w);
|
||||
}
|
||||
w.push(b')');
|
||||
return;
|
||||
}
|
||||
};
|
||||
w.push(c);
|
||||
}
|
||||
|
||||
pub fn parse<'a>(&self, parser: &mut Parser<'a>) -> Result<Variant<'a>, DbusError> {
|
||||
let var = match self {
|
||||
DynamicType::U8 => Variant::U8(parser.read_pod()?),
|
||||
DynamicType::Bool => Variant::Bool(parser.read_bool()?),
|
||||
DynamicType::I16 => Variant::I16(parser.read_pod()?),
|
||||
DynamicType::U16 => Variant::U16(parser.read_pod()?),
|
||||
DynamicType::I32 => Variant::I32(parser.read_pod()?),
|
||||
DynamicType::U32 => Variant::U32(parser.read_pod()?),
|
||||
DynamicType::I64 => Variant::I64(parser.read_pod()?),
|
||||
DynamicType::U64 => Variant::U64(parser.read_pod()?),
|
||||
DynamicType::F64 => Variant::F64(f64::from_bits(parser.read_pod()?)),
|
||||
DynamicType::String => Variant::String(parser.read_string()?),
|
||||
DynamicType::ObjectPath => Variant::ObjectPath(parser.read_object_path()?),
|
||||
DynamicType::Signature => Variant::Signature(parser.read_signature()?),
|
||||
DynamicType::Variant => Variant::Variant(Box::new(parser.read_variant()?)),
|
||||
DynamicType::Fd => Variant::Fd(parser.read_fd()?),
|
||||
DynamicType::Array(el) => {
|
||||
let len: u32 = parser.read_pod()?;
|
||||
parser.align_to(el.alignment())?;
|
||||
let len = len as usize;
|
||||
if parser.buf.len() - parser.pos < len {
|
||||
return Err(DbusError::UnexpectedEof);
|
||||
}
|
||||
let mut vals = vec![];
|
||||
{
|
||||
let mut parser = Parser {
|
||||
buf: &parser.buf[..parser.pos + len],
|
||||
pos: parser.pos,
|
||||
fds: parser.fds,
|
||||
};
|
||||
while !parser.eof() {
|
||||
vals.push(el.parse(&mut parser)?);
|
||||
}
|
||||
}
|
||||
parser.pos += len;
|
||||
Variant::Array(el.deref().clone(), vals)
|
||||
}
|
||||
DynamicType::DictEntry(k, v) => {
|
||||
parser.align_to(8)?;
|
||||
Variant::DictEntry(Box::new(k.parse(parser)?), Box::new(v.parse(parser)?))
|
||||
}
|
||||
DynamicType::Struct(fields) => {
|
||||
let mut vals = vec![];
|
||||
parser.align_to(8)?;
|
||||
for field in fields {
|
||||
vals.push(field.parse(parser)?);
|
||||
}
|
||||
Variant::Struct(vals)
|
||||
}
|
||||
};
|
||||
Ok(var)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
dbus::{DbusType, Formatter, types::Variant},
|
||||
utils::buf::DynamicBuf,
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::{OwnedFd, Packed},
|
||||
};
|
||||
|
||||
impl<'a> Formatter<'a> {
|
||||
pub fn new(fds: &'a mut Vec<Rc<OwnedFd>>, buf: &'a mut DynamicBuf) -> Self {
|
||||
Self { fds, buf }
|
||||
}
|
||||
|
||||
pub fn marshal<'b, T: DbusType<'b>>(&mut self, t: &T) {
|
||||
t.marshal(self)
|
||||
}
|
||||
|
||||
pub fn pad_to(&mut self, alignment: usize) {
|
||||
static BUF: [u8; 8] = [0; 8];
|
||||
let len = self.buf.len().wrapping_neg() & (alignment - 1);
|
||||
self.buf.extend_from_slice(&BUF[..len]);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
pub fn write_packed<'b, T: DbusType<'b> + Packed>(&mut self, t: &T) {
|
||||
self.pad_to(T::ALIGNMENT);
|
||||
self.buf.extend_from_slice(uapi::as_bytes(t));
|
||||
}
|
||||
|
||||
pub fn write_str(&mut self, s: &str) {
|
||||
self.write_packed(&(s.len() as u32));
|
||||
self.buf.extend_from_slice(s.as_bytes());
|
||||
self.buf.push(0);
|
||||
}
|
||||
|
||||
pub fn write_signature(&mut self, s: &[u8]) {
|
||||
self.write_packed(&(s.len() as u8));
|
||||
self.buf.extend_from_slice(s);
|
||||
self.buf.push(0);
|
||||
}
|
||||
|
||||
pub fn write_array<'b, T: DbusType<'b>>(&mut self, a: &[T]) {
|
||||
self.pad_to(4);
|
||||
let len_pos = self.buf.len();
|
||||
self.write_packed(&0u32);
|
||||
self.pad_to(T::ALIGNMENT);
|
||||
let start = self.buf.len();
|
||||
for v in a {
|
||||
v.marshal(self);
|
||||
}
|
||||
let len = (self.buf.len() - start) as u32;
|
||||
self.buf[len_pos..len_pos + 4].copy_from_slice(uapi::as_bytes(&len));
|
||||
}
|
||||
|
||||
pub fn write_fd(&mut self, fd: &Rc<OwnedFd>) {
|
||||
self.write_packed(&(self.fds.len() as u32));
|
||||
self.fds.push(fd.clone());
|
||||
}
|
||||
|
||||
pub fn write_variant(&mut self, variant: &Variant) {
|
||||
let pos = self.buf.len();
|
||||
self.buf.push(0);
|
||||
variant.write_signature(self.buf);
|
||||
self.buf.push(0);
|
||||
self.buf[pos] = (self.buf.len() - pos - 2) as u8;
|
||||
self.write_variant_body(variant);
|
||||
}
|
||||
|
||||
pub fn write_variant_body(&mut self, variant: &Variant) {
|
||||
match variant {
|
||||
Variant::U8(v) => v.marshal(self),
|
||||
Variant::Bool(v) => v.marshal(self),
|
||||
Variant::I16(v) => v.marshal(self),
|
||||
Variant::U16(v) => v.marshal(self),
|
||||
Variant::I32(v) => v.marshal(self),
|
||||
Variant::U32(v) => v.marshal(self),
|
||||
Variant::I64(v) => v.marshal(self),
|
||||
Variant::U64(v) => v.marshal(self),
|
||||
Variant::F64(v) => v.marshal(self),
|
||||
Variant::String(v) => v.marshal(self),
|
||||
Variant::ObjectPath(v) => v.marshal(self),
|
||||
Variant::Signature(v) => v.marshal(self),
|
||||
Variant::Variant(v) => v.marshal(self),
|
||||
Variant::Fd(f) => f.marshal(self),
|
||||
Variant::Array(el, v) => {
|
||||
self.pad_to(4);
|
||||
let len_pos = self.buf.len();
|
||||
self.write_packed(&0u32);
|
||||
self.pad_to(el.alignment());
|
||||
let start = self.buf.len();
|
||||
for v in v {
|
||||
self.write_variant_body(v);
|
||||
}
|
||||
let len = (self.buf.len() - start) as u32;
|
||||
self.buf[len_pos..len_pos + 4].copy_from_slice(uapi::as_bytes(&len));
|
||||
}
|
||||
Variant::DictEntry(k, v) => {
|
||||
self.pad_to(8);
|
||||
self.write_variant_body(k);
|
||||
self.write_variant_body(v);
|
||||
}
|
||||
Variant::Struct(f) => {
|
||||
self.pad_to(8);
|
||||
for v in f {
|
||||
self.write_variant_body(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +68,6 @@ async fn connect(
|
|||
headers: Default::default(),
|
||||
run_toplevel: run_toplevel.clone(),
|
||||
signal_handlers: Default::default(),
|
||||
objects: Default::default(),
|
||||
});
|
||||
let skt = socket.clone();
|
||||
socket.call(
|
||||
|
|
|
|||
|
|
@ -6,16 +6,13 @@ use {
|
|||
crate::{
|
||||
dbus::{
|
||||
CallError, DbusError, DbusSocket, Headers, MSG_ERROR, MSG_METHOD_CALL,
|
||||
MSG_METHOD_RETURN, MSG_SIGNAL, MemberHandlerKey, Message, MethodHandlerApi,
|
||||
NO_REPLY_EXPECTED, Parser, PropertyGetAllHandlerProxy, PropertyGetHandlerProxy,
|
||||
MSG_METHOD_RETURN, MSG_SIGNAL, Parser,
|
||||
},
|
||||
utils::{
|
||||
bitflags::BitflagsExt,
|
||||
bufio::BufIoIncoming,
|
||||
errorfmt::ErrorFmt,
|
||||
ptr_ext::{MutPtrExt, PtrExt},
|
||||
},
|
||||
wire_dbus::org::freedesktop::dbus::properties::{Get, GetAll},
|
||||
},
|
||||
std::{cell::UnsafeCell, ops::Deref, rc::Rc},
|
||||
};
|
||||
|
|
@ -64,7 +61,6 @@ impl Incoming {
|
|||
return Err(DbusError::InvalidEndianess);
|
||||
}
|
||||
let msg_ty = msg_buf[1];
|
||||
let flags = msg_buf[2];
|
||||
let protocol = msg_buf[3];
|
||||
if protocol != 1 {
|
||||
return Err(DbusError::InvalidProtocol);
|
||||
|
|
@ -87,73 +83,16 @@ impl Incoming {
|
|||
return Err(DbusError::TooFewFds);
|
||||
}
|
||||
let fds: Vec<_> = self.incoming.fds.drain(..unix_fds).collect();
|
||||
let mut parser = Parser {
|
||||
buf: msg_buf,
|
||||
pos: FIXED_HEADER_SIZE + dyn_header_len as usize,
|
||||
fds: &fds,
|
||||
};
|
||||
let mut parser =
|
||||
Parser::new_at(msg_buf, FIXED_HEADER_SIZE + dyn_header_len as usize, &fds);
|
||||
match msg_ty {
|
||||
MSG_METHOD_CALL => {
|
||||
let (sender, interface, member, path) = match (
|
||||
&headers.sender,
|
||||
&headers.interface,
|
||||
&headers.member,
|
||||
&headers.path,
|
||||
) {
|
||||
(Some(s), Some(i), Some(m), Some(p)) => (s, i, m, p),
|
||||
let sender = match &headers.sender {
|
||||
Some(s) => s,
|
||||
_ => return Err(DbusError::MissingMethodCallHeaders),
|
||||
};
|
||||
if let Some(object) = self.socket.objects.get(path.deref()) {
|
||||
let method_handler;
|
||||
let handler: Option<&dyn MethodHandlerApi> =
|
||||
if (interface.deref(), member.deref()) == (Get::INTERFACE, Get::MEMBER) {
|
||||
Some(&PropertyGetHandlerProxy)
|
||||
} else if (interface.deref(), member.deref())
|
||||
== (GetAll::INTERFACE, GetAll::MEMBER)
|
||||
{
|
||||
Some(&PropertyGetAllHandlerProxy)
|
||||
} else {
|
||||
let key = MemberHandlerKey {
|
||||
interface: interface.deref(),
|
||||
member: member.deref(),
|
||||
};
|
||||
method_handler = object.methods.get(&key);
|
||||
method_handler.as_deref()
|
||||
};
|
||||
if let Some(handler) = handler {
|
||||
let sig = headers.signature.as_deref().unwrap_or("");
|
||||
if sig != handler.signature() {
|
||||
let msg = format!(
|
||||
"Method call has an invalid signature: expected: {}, actual: {}",
|
||||
handler.signature(),
|
||||
sig,
|
||||
);
|
||||
self.socket.send_error(sender.deref(), serial, &msg);
|
||||
} else {
|
||||
let reply_expected = !flags.contains(NO_REPLY_EXPECTED);
|
||||
if let Err(e) = handler.handle(
|
||||
&object,
|
||||
&self.socket,
|
||||
sender,
|
||||
serial,
|
||||
reply_expected,
|
||||
&mut parser,
|
||||
) {
|
||||
log::error!(
|
||||
"{}: Could not handle method call: {}",
|
||||
self.socket.bus_name,
|
||||
ErrorFmt(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.socket
|
||||
.send_error(sender.deref(), serial, "Method does not exist");
|
||||
}
|
||||
} else {
|
||||
self.socket
|
||||
.send_error(sender.deref(), serial, "Object does not exist");
|
||||
}
|
||||
self.socket
|
||||
.send_error(sender.deref(), serial, "Object does not exist");
|
||||
}
|
||||
MSG_METHOD_RETURN | MSG_ERROR => {
|
||||
let serial = match headers.reply_serial {
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
use {
|
||||
crate::dbus::{
|
||||
DbusError, DbusType, DynamicType, Parser,
|
||||
types::{Bool, FALSE, ObjectPath, Signature, TRUE, Variant},
|
||||
},
|
||||
bstr::ByteSlice,
|
||||
std::{borrow::Cow, rc::Rc},
|
||||
uapi::{OwnedFd, Pod},
|
||||
};
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new(buf: &'a [u8], fds: &'a [Rc<OwnedFd>]) -> Self {
|
||||
Self { buf, pos: 0, fds }
|
||||
}
|
||||
|
||||
pub fn eof(&self) -> bool {
|
||||
self.pos == self.buf.len()
|
||||
}
|
||||
|
||||
pub fn unmarshal<T: DbusType<'a>>(&mut self) -> Result<T, DbusError> {
|
||||
T::unmarshal(self)
|
||||
}
|
||||
|
||||
pub fn align_to(&mut self, n: usize) -> Result<(), DbusError> {
|
||||
let new = self.pos + (self.pos.wrapping_neg() & (n - 1));
|
||||
if new > self.buf.len() {
|
||||
return Err(DbusError::UnexpectedEof);
|
||||
}
|
||||
self.pos = new;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_fd(&mut self) -> Result<Rc<OwnedFd>, DbusError> {
|
||||
let idx: u32 = self.read_pod()?;
|
||||
let idx = idx as usize;
|
||||
if idx >= self.fds.len() {
|
||||
return Err(DbusError::OobFds);
|
||||
}
|
||||
Ok(self.fds[idx].clone())
|
||||
}
|
||||
|
||||
pub fn read_pod<'b, T: DbusType<'b> + Pod>(&mut self) -> Result<T, DbusError> {
|
||||
self.align_to(T::ALIGNMENT)?;
|
||||
match uapi::pod_read_init(&self.buf[self.pos..]) {
|
||||
Ok(v) => {
|
||||
self.pos += size_of::<T>();
|
||||
Ok(v)
|
||||
}
|
||||
_ => Err(DbusError::UnexpectedEof),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_bool(&mut self) -> Result<Bool, DbusError> {
|
||||
let v: u32 = self.read_pod()?;
|
||||
match v {
|
||||
0 => Ok(FALSE),
|
||||
1 => Ok(TRUE),
|
||||
_ => Err(DbusError::InvalidBoolValue),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_object_path(&mut self) -> Result<ObjectPath<'a>, DbusError> {
|
||||
self.read_string().map(ObjectPath)
|
||||
}
|
||||
|
||||
pub fn read_string(&mut self) -> Result<Cow<'a, str>, DbusError> {
|
||||
let len: u32 = self.read_pod()?;
|
||||
let s = self.read_string_(len as usize)?;
|
||||
Ok(Cow::Borrowed(s))
|
||||
}
|
||||
|
||||
pub fn read_signature(&mut self) -> Result<Signature<'a>, DbusError> {
|
||||
let len: u8 = self.read_pod()?;
|
||||
let s = self.read_string_(len as usize)?;
|
||||
Ok(Signature(Cow::Borrowed(s)))
|
||||
}
|
||||
|
||||
fn read_string_(&mut self, len: usize) -> Result<&'a str, DbusError> {
|
||||
if len == usize::MAX || self.buf.len() - self.pos < len + 1 {
|
||||
return Err(DbusError::UnexpectedEof);
|
||||
}
|
||||
let s = &self.buf[self.pos..self.pos + len];
|
||||
self.pos += len + 1;
|
||||
match s.to_str() {
|
||||
Ok(s) => Ok(s),
|
||||
_ => Err(DbusError::InvalidUtf8),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_array<T: DbusType<'a>>(&mut self) -> Result<Cow<'a, [T]>, DbusError> {
|
||||
let len: u32 = self.read_pod()?;
|
||||
let len = len as usize;
|
||||
self.align_to(T::ALIGNMENT)?;
|
||||
if self.buf.len() - self.pos < len {
|
||||
return Err(DbusError::UnexpectedEof);
|
||||
}
|
||||
if T::IS_POD {
|
||||
if len % size_of::<T>() != 0 {
|
||||
return Err(DbusError::PodArrayLength);
|
||||
}
|
||||
let slice = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
self.buf[self.pos..].as_ptr() as *const T,
|
||||
len / size_of::<T>(),
|
||||
)
|
||||
};
|
||||
self.pos += len;
|
||||
Ok(Cow::Borrowed(slice))
|
||||
} else {
|
||||
let mut parser = Parser {
|
||||
buf: &self.buf[..self.pos + len],
|
||||
pos: self.pos,
|
||||
fds: self.fds,
|
||||
};
|
||||
self.pos += len;
|
||||
let mut res = vec![];
|
||||
while !parser.eof() {
|
||||
res.push(T::unmarshal(&mut parser)?);
|
||||
}
|
||||
Ok(Cow::Owned(res))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_variant(&mut self) -> Result<Variant<'a>, DbusError> {
|
||||
let sig = self.read_signature()?;
|
||||
let sig = sig.0.as_bytes();
|
||||
let (parser, rem) = DynamicType::from_signature(sig)?;
|
||||
if rem.len() > 0 {
|
||||
return Err(DbusError::TrailingVariantSignature);
|
||||
}
|
||||
parser.parse(self)
|
||||
}
|
||||
|
||||
pub fn read_variant_as<T: DbusType<'a>>(&mut self) -> Result<T, DbusError> {
|
||||
let sig = self.read_signature()?;
|
||||
let mut sig = sig.0.as_bytes();
|
||||
T::consume_signature(&mut sig)?;
|
||||
if sig.len() > 0 {
|
||||
return Err(DbusError::TrailingVariantSignature);
|
||||
}
|
||||
T::unmarshal(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
use {
|
||||
crate::dbus::{DbusError, DbusType, Formatter, Message, MethodCall, Parser},
|
||||
std::{borrow::Cow, marker::PhantomData},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Get<'a, T: DbusType<'static>> {
|
||||
pub interface_name: Cow<'a, str>,
|
||||
pub property_name: Cow<'a, str>,
|
||||
pub _phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: DbusType<'static>> Message<'a> for Get<'a, T> {
|
||||
const SIGNATURE: &'static str = "ss";
|
||||
const INTERFACE: &'static str = "org.freedesktop.DBus.Properties";
|
||||
const MEMBER: &'static str = "Get";
|
||||
type Generic<'b> = Get<'b, T>;
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.marshal(&self.interface_name);
|
||||
fmt.marshal(&self.property_name);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
Ok(Self {
|
||||
interface_name: parser.unmarshal()?,
|
||||
property_name: parser.unmarshal()?,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: DbusType<'static>> MethodCall<'a> for Get<'a, T> {
|
||||
type Reply = GetReply<'static, T>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GetReply<'a, T: DbusType<'a>> {
|
||||
pub value: T,
|
||||
pub _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: DbusType<'a>> Message<'a> for GetReply<'a, T> {
|
||||
const SIGNATURE: &'static str = "v";
|
||||
const INTERFACE: &'static str = "org.freedesktop.DBus.Properties";
|
||||
const MEMBER: &'static str = "Get";
|
||||
type Generic<'b> = GetReply<'b, T::Generic<'b>>;
|
||||
|
||||
fn marshal(&self, _fmt: &mut Formatter) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
Ok(Self {
|
||||
value: parser.read_variant_as()?,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
self.value.num_fds()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
use {
|
||||
crate::{
|
||||
dbus::{
|
||||
AsyncProperty, AsyncReply, AsyncReplySlot, BUS_DEST, BUS_PATH, DbusError, DbusObject,
|
||||
DbusObjectData, DbusSocket, DbusType, ErrorMessage, Formatter, HDR_DESTINATION,
|
||||
HDR_ERROR_NAME, HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_REPLY_SERIAL, HDR_SIGNATURE,
|
||||
HDR_UNIX_FDS, Headers, InterfaceSignalHandlers, MSG_ERROR, MSG_METHOD_CALL,
|
||||
MSG_METHOD_RETURN, MSG_SIGNAL, Message, MethodCall, NO_REPLY_EXPECTED, Parser,
|
||||
Property, Reply, ReplyHandler, Signal, SignalHandler, SignalHandlerApi,
|
||||
SignalHandlerData,
|
||||
AsyncProperty, AsyncReply, AsyncReplySlot, BUS_DEST, BUS_PATH, DbusError, DbusSocket,
|
||||
DbusType, ErrorMessage, Formatter, HDR_DESTINATION, HDR_ERROR_NAME, HDR_INTERFACE,
|
||||
HDR_MEMBER, HDR_PATH, HDR_REPLY_SERIAL, HDR_SIGNATURE, HDR_UNIX_FDS, Headers,
|
||||
InterfaceSignalHandlers, MSG_ERROR, MSG_METHOD_CALL, Message,
|
||||
MethodCall, NO_REPLY_EXPECTED, Parser, Property, Reply, ReplyHandler, Signal,
|
||||
SignalHandler, SignalHandlerApi, SignalHandlerData,
|
||||
property::Get,
|
||||
types::{ObjectPath, Signature, Variant},
|
||||
},
|
||||
|
|
@ -15,8 +14,8 @@ use {
|
|||
wire_dbus::org,
|
||||
},
|
||||
std::{
|
||||
borrow::Cow, cell::Cell, collections::hash_map::Entry, fmt::Write, marker::PhantomData,
|
||||
mem, ops::DerefMut, rc::Rc,
|
||||
cell::Cell, collections::hash_map::Entry, fmt::Write, marker::PhantomData, mem,
|
||||
ops::DerefMut, rc::Rc,
|
||||
},
|
||||
uapi::c,
|
||||
};
|
||||
|
|
@ -28,7 +27,6 @@ impl DbusSocket {
|
|||
self.outgoing_.take();
|
||||
self.reply_handlers.clear();
|
||||
self.signal_handlers.borrow_mut().clear();
|
||||
self.objects.clear();
|
||||
}
|
||||
|
||||
pub(super) fn kill(self: &Rc<Self>) {
|
||||
|
|
@ -134,28 +132,6 @@ impl DbusSocket {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_object(
|
||||
self: &Rc<Self>,
|
||||
object: impl Into<Cow<'static, str>>,
|
||||
) -> Result<DbusObject, DbusError> {
|
||||
let object = object.into();
|
||||
let data = Rc::new(DbusObjectData {
|
||||
path: object.clone(),
|
||||
methods: Default::default(),
|
||||
properties: Default::default(),
|
||||
});
|
||||
match self.objects.lock().entry(object) {
|
||||
Entry::Occupied(_) => Err(DbusError::AlreadyHandled),
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(data.clone());
|
||||
Ok(DbusObject {
|
||||
socket: self.clone(),
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_signal<T, F>(
|
||||
self: &Rc<Self>,
|
||||
sender: Option<&str>,
|
||||
|
|
@ -268,29 +244,12 @@ impl DbusSocket {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn emit_signal<'a, T: Signal<'a>>(&self, path: &str, msg: &T) -> u32 {
|
||||
let (msg, serial) = self.format_signal(path, msg);
|
||||
self.bufio.send(msg);
|
||||
serial
|
||||
}
|
||||
|
||||
pub fn send_error(&self, destination: &str, reply_serial: u32, msg: &str) -> u32 {
|
||||
let (msg, serial) = self.format_error(destination, reply_serial, msg);
|
||||
self.bufio.send(msg);
|
||||
serial
|
||||
}
|
||||
|
||||
pub fn send_reply<'a, T: Message<'a>>(
|
||||
&self,
|
||||
destination: &str,
|
||||
reply_serial: u32,
|
||||
msg: &T,
|
||||
) -> u32 {
|
||||
let (msg, serial) = self.format_reply(destination, reply_serial, msg);
|
||||
self.bufio.send(msg);
|
||||
serial
|
||||
}
|
||||
|
||||
fn send_call<'a, T: Message<'a>>(
|
||||
&self,
|
||||
path: &str,
|
||||
|
|
@ -303,10 +262,6 @@ impl DbusSocket {
|
|||
serial
|
||||
}
|
||||
|
||||
fn format_signal<'a, T: Signal<'a>>(&self, path: &str, msg: &T) -> (BufIoMessage, u32) {
|
||||
self.format_generic(MSG_SIGNAL, Some(path), None, None, 0, msg, None, true, true)
|
||||
}
|
||||
|
||||
fn format_error(&self, destination: &str, reply_serial: u32, msg: &str) -> (BufIoMessage, u32) {
|
||||
let em = ErrorMessage { msg: msg.into() };
|
||||
self.format_generic(
|
||||
|
|
@ -322,25 +277,6 @@ impl DbusSocket {
|
|||
)
|
||||
}
|
||||
|
||||
fn format_reply<'a, T: Message<'a>>(
|
||||
&self,
|
||||
destination: &str,
|
||||
reply_serial: u32,
|
||||
msg: &T,
|
||||
) -> (BufIoMessage, u32) {
|
||||
self.format_generic(
|
||||
MSG_METHOD_RETURN,
|
||||
None,
|
||||
Some(reply_serial),
|
||||
Some(destination),
|
||||
0,
|
||||
msg,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
fn format_call<'a, T: Message<'a>>(
|
||||
&self,
|
||||
path: &str,
|
||||
|
|
|
|||
|
|
@ -1,567 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
dbus::{
|
||||
DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE,
|
||||
TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING,
|
||||
TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT,
|
||||
},
|
||||
utils::buf::DynamicBuf,
|
||||
},
|
||||
std::{borrow::Cow, ops::Deref, rc::Rc},
|
||||
uapi::{OwnedFd, Packed, Pod},
|
||||
};
|
||||
|
||||
macro_rules! consume_signature_body {
|
||||
($s:expr, $ty:expr) => {{
|
||||
if $s.is_empty() {
|
||||
return Err(DbusError::EmptySignature);
|
||||
}
|
||||
if $s[0] != $ty {
|
||||
return Err(DbusError::InvalidSignatureType);
|
||||
}
|
||||
*$s = &(*$s)[1..];
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! signature {
|
||||
($ty:expr) => {
|
||||
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> {
|
||||
consume_signature_body!(s, $ty);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_signature(w: &mut Vec<u8>) {
|
||||
w.push(TY_BYTE);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for u8 {
|
||||
const ALIGNMENT: usize = 1;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_BYTE);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Bool(u32);
|
||||
pub const FALSE: Bool = Bool(0);
|
||||
pub const TRUE: Bool = Bool(1);
|
||||
|
||||
unsafe impl Pod for Bool {}
|
||||
unsafe impl Packed for Bool {}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for Bool {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_BOOLEAN);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_bool()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for i16 {
|
||||
const ALIGNMENT: usize = 2;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_INT16);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for u16 {
|
||||
const ALIGNMENT: usize = 2;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_UINT16);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for i32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_INT32);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for u32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_UINT32);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for i64 {
|
||||
const ALIGNMENT: usize = 8;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_INT64);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for u64 {
|
||||
const ALIGNMENT: usize = 8;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_UINT64);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for f64 {
|
||||
const ALIGNMENT: usize = 8;
|
||||
const IS_POD: bool = true;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_DOUBLE);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_packed(&self.to_bits());
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
Ok(f64::from_bits(parser.read_pod()?))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for Cow<'a, str> {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = Cow<'b, str>;
|
||||
|
||||
signature!(TY_STRING);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_str(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Signature<'a>(pub Cow<'a, str>);
|
||||
|
||||
impl<'a> Deref for Signature<'a> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for Signature<'a> {
|
||||
const ALIGNMENT: usize = 1;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = Signature<'b>;
|
||||
|
||||
signature!(TY_SIGNATURE);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_signature(self.0.as_bytes());
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_signature()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ObjectPath<'a>(pub Cow<'a, str>);
|
||||
|
||||
impl<'a> Deref for ObjectPath<'a> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for ObjectPath<'a> {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = ObjectPath<'b>;
|
||||
|
||||
signature!(TY_OBJECT_PATH);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_str(&self.0);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_object_path()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for Rc<OwnedFd> {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = Self;
|
||||
|
||||
signature!(TY_UNIX_FD);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_fd(self)
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_fd()
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: DbusType<'a>> DbusType<'a> for Cow<'a, [T]> {
|
||||
const ALIGNMENT: usize = 4;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = Cow<'b, [T::Generic<'b>]>;
|
||||
|
||||
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> {
|
||||
consume_signature_body!(s, TY_ARRAY);
|
||||
T::consume_signature(s)
|
||||
}
|
||||
|
||||
fn write_signature(w: &mut Vec<u8>) {
|
||||
w.push(TY_ARRAY);
|
||||
T::write_signature(w);
|
||||
}
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_array(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_array()
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
let mut res = 0;
|
||||
for t in self.deref() {
|
||||
res += t.num_fds();
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct DictEntry<K, V> {
|
||||
pub key: K,
|
||||
pub value: V,
|
||||
}
|
||||
|
||||
unsafe impl<'a, K: DbusType<'a>, V: DbusType<'a>> DbusType<'a> for DictEntry<K, V> {
|
||||
const ALIGNMENT: usize = 8;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = DictEntry<K::Generic<'b>, V::Generic<'b>>;
|
||||
|
||||
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> {
|
||||
consume_signature_body!(s, b'{');
|
||||
K::consume_signature(s)?;
|
||||
V::consume_signature(s)?;
|
||||
consume_signature_body!(s, b'}');
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_signature(w: &mut Vec<u8>) {
|
||||
w.push(b'{');
|
||||
K::write_signature(w);
|
||||
V::write_signature(w);
|
||||
w.push(b'}');
|
||||
}
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.pad_to(8);
|
||||
self.key.marshal(fmt);
|
||||
self.value.marshal(fmt);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.align_to(8)?;
|
||||
Ok(Self {
|
||||
key: K::unmarshal(parser)?,
|
||||
value: V::unmarshal(parser)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuple {
|
||||
($($p:ident),*) => {
|
||||
#[expect(non_snake_case)]
|
||||
unsafe impl<'a, $($p: DbusType<'a>),*> DbusType<'a> for ($($p,)*) {
|
||||
const ALIGNMENT: usize = 8;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = ($($p::Generic<'b>,)*);
|
||||
|
||||
fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> {
|
||||
consume_signature_body!(s, b'(');
|
||||
$(
|
||||
$p::consume_signature(s)?;
|
||||
)*
|
||||
consume_signature_body!(s, b')');
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_signature(w: &mut Vec<u8>) {
|
||||
w.push(b'(');
|
||||
$(
|
||||
$p::write_signature(w);
|
||||
)*
|
||||
w.push(b')');
|
||||
}
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
let ($($p,)*) = self;
|
||||
fmt.pad_to(8);
|
||||
$(
|
||||
$p.marshal(fmt);
|
||||
)*
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.align_to(8)?;
|
||||
Ok(($($p::unmarshal(parser)?,)*))
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
let mut res = 0;
|
||||
let ($($p,)*) = self;
|
||||
$(
|
||||
res += $p.num_fds();
|
||||
)*
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tuple!(A);
|
||||
tuple!(A, B);
|
||||
tuple!(A, B, C);
|
||||
tuple!(A, B, C, D);
|
||||
tuple!(A, B, C, D, E);
|
||||
tuple!(A, B, C, D, E, F);
|
||||
tuple!(A, B, C, D, E, F, G);
|
||||
tuple!(A, B, C, D, E, F, G, H);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Variant<'a> {
|
||||
U8(u8),
|
||||
Bool(Bool),
|
||||
I16(i16),
|
||||
U16(u16),
|
||||
I32(i32),
|
||||
U32(u32),
|
||||
I64(i64),
|
||||
U64(u64),
|
||||
F64(f64),
|
||||
String(Cow<'a, str>),
|
||||
ObjectPath(ObjectPath<'a>),
|
||||
Signature(Signature<'a>),
|
||||
Variant(Box<Variant<'a>>),
|
||||
Fd(Rc<OwnedFd>),
|
||||
Array(DynamicType, Vec<Variant<'a>>),
|
||||
DictEntry(Box<Variant<'a>>, Box<Variant<'a>>),
|
||||
Struct(Vec<Variant<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> Variant<'a> {
|
||||
pub fn into_string(self) -> Result<Cow<'a, str>, DbusError> {
|
||||
match self {
|
||||
Variant::String(s) => Ok(s),
|
||||
_ => Err(DbusError::InvalidVariantType),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_object_path(self) -> Result<ObjectPath<'a>, DbusError> {
|
||||
match self {
|
||||
Variant::ObjectPath(s) => Ok(s),
|
||||
_ => Err(DbusError::InvalidVariantType),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_signature(self) -> Result<Signature<'a>, DbusError> {
|
||||
match self {
|
||||
Variant::Signature(s) => Ok(s),
|
||||
_ => Err(DbusError::InvalidVariantType),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_u32(self) -> Result<u32, DbusError> {
|
||||
match self {
|
||||
Variant::U32(s) => Ok(s),
|
||||
_ => Err(DbusError::InvalidVariantType),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_signature(&self, w: &mut DynamicBuf) {
|
||||
let c = match self {
|
||||
Variant::U8(..) => TY_BYTE,
|
||||
Variant::Bool(..) => TY_BOOLEAN,
|
||||
Variant::I16(..) => TY_INT16,
|
||||
Variant::U16(..) => TY_UINT16,
|
||||
Variant::I32(..) => TY_INT32,
|
||||
Variant::U32(..) => TY_UINT32,
|
||||
Variant::I64(..) => TY_INT64,
|
||||
Variant::U64(..) => TY_UINT64,
|
||||
Variant::F64(..) => TY_DOUBLE,
|
||||
Variant::String(..) => TY_STRING,
|
||||
Variant::ObjectPath(..) => TY_OBJECT_PATH,
|
||||
Variant::Signature(..) => TY_SIGNATURE,
|
||||
Variant::Variant(..) => TY_VARIANT,
|
||||
Variant::Fd(..) => TY_UNIX_FD,
|
||||
Variant::Array(el, _) => {
|
||||
w.push(TY_ARRAY);
|
||||
el.write_signature(w);
|
||||
return;
|
||||
}
|
||||
Variant::DictEntry(k, v) => {
|
||||
w.push(b'{');
|
||||
k.write_signature(w);
|
||||
v.write_signature(w);
|
||||
w.push(b'}');
|
||||
return;
|
||||
}
|
||||
Variant::Struct(f) => {
|
||||
w.push(b'(');
|
||||
for f in f {
|
||||
f.write_signature(w);
|
||||
}
|
||||
w.push(b')');
|
||||
return;
|
||||
}
|
||||
};
|
||||
w.push(c);
|
||||
}
|
||||
|
||||
pub fn borrow<'b>(&'b self) -> Variant<'b> {
|
||||
match self {
|
||||
Variant::U8(v) => Variant::U8(*v),
|
||||
Variant::Bool(v) => Variant::Bool(*v),
|
||||
Variant::I16(v) => Variant::I16(*v),
|
||||
Variant::U16(v) => Variant::U16(*v),
|
||||
Variant::I32(v) => Variant::I32(*v),
|
||||
Variant::U32(v) => Variant::U32(*v),
|
||||
Variant::I64(v) => Variant::I64(*v),
|
||||
Variant::U64(v) => Variant::U64(*v),
|
||||
Variant::F64(v) => Variant::F64(*v),
|
||||
Variant::String(v) => Variant::String(v.deref().into()),
|
||||
Variant::ObjectPath(v) => Variant::ObjectPath(ObjectPath(v.0.deref().into())),
|
||||
Variant::Signature(v) => Variant::Signature(Signature(v.0.deref().into())),
|
||||
Variant::Variant(v) => Variant::Variant(Box::new(v.deref().borrow())),
|
||||
Variant::Fd(v) => Variant::Fd(v.clone()),
|
||||
Variant::Array(t, v) => {
|
||||
Variant::Array(t.clone(), v.iter().map(|v| v.borrow()).collect())
|
||||
}
|
||||
Variant::DictEntry(k, v) => {
|
||||
Variant::DictEntry(Box::new(k.deref().borrow()), Box::new(v.deref().borrow()))
|
||||
}
|
||||
Variant::Struct(v) => Variant::Struct(v.iter().map(|v| v.borrow()).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> DbusType<'a> for Variant<'a> {
|
||||
const ALIGNMENT: usize = 1;
|
||||
const IS_POD: bool = false;
|
||||
type Generic<'b> = Variant<'b>;
|
||||
|
||||
signature!(TY_VARIANT);
|
||||
|
||||
fn marshal(&self, fmt: &mut Formatter) {
|
||||
fmt.write_variant(self);
|
||||
}
|
||||
|
||||
fn unmarshal(parser: &mut Parser<'a>) -> Result<Self, DbusError> {
|
||||
parser.read_variant()
|
||||
}
|
||||
|
||||
fn num_fds(&self) -> u32 {
|
||||
match self {
|
||||
Variant::U8(_) => 0,
|
||||
Variant::Bool(_) => 0,
|
||||
Variant::I16(_) => 0,
|
||||
Variant::U16(_) => 0,
|
||||
Variant::I32(_) => 0,
|
||||
Variant::U32(_) => 0,
|
||||
Variant::I64(_) => 0,
|
||||
Variant::U64(_) => 0,
|
||||
Variant::F64(_) => 0,
|
||||
Variant::String(_) => 0,
|
||||
Variant::ObjectPath(_) => 0,
|
||||
Variant::Signature(_) => 0,
|
||||
Variant::Variant(v) => v.num_fds(),
|
||||
Variant::Array(_, a) => a.iter().map(|e| e.num_fds()).sum(),
|
||||
Variant::DictEntry(k, v) => k.num_fds() + v.num_fds(),
|
||||
Variant::Struct(f) => f.iter().map(|f| f.num_fds()).sum(),
|
||||
Variant::Fd(_) => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,124 +1,17 @@
|
|||
use {
|
||||
crate::{gfx_api::GfxContext, utils::oserror::OsError, video::Modifier},
|
||||
ahash::AHashMap,
|
||||
byteorder::{NativeEndian, WriteBytesExt},
|
||||
std::{io::Write, rc::Rc},
|
||||
thiserror::Error,
|
||||
uapi::{OwnedFd, c},
|
||||
};
|
||||
pub use jay_drm_feedback::*;
|
||||
|
||||
linear_ids!(DrmFeedbackIds, DrmFeedbackId);
|
||||
use crate::{gfx_api::GfxContext, video::Modifier};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmFeedbackShared {
|
||||
pub fd: Rc<OwnedFd>,
|
||||
pub size: usize,
|
||||
pub main_device: c::dev_t,
|
||||
pub indices: AHashMap<(u32, Modifier), u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmFeedback {
|
||||
pub id: DrmFeedbackId,
|
||||
pub shared: Rc<DrmFeedbackShared>,
|
||||
pub tranches: Vec<DrmFeedbackTranche>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DrmFeedbackTranche {
|
||||
pub device: c::dev_t,
|
||||
pub indices: Vec<u16>,
|
||||
pub scanout: bool,
|
||||
}
|
||||
|
||||
impl DrmFeedback {
|
||||
pub fn new(
|
||||
ids: &DrmFeedbackIds,
|
||||
render_ctx: &dyn GfxContext,
|
||||
) -> Result<Self, DrmFeedbackError> {
|
||||
let main_device = match render_ctx.allocator().drm() {
|
||||
Some(drm) => drm.dev(),
|
||||
_ => return Err(DrmFeedbackError::NoDrmDevice),
|
||||
};
|
||||
let (data, index_map) = create_fd_data(render_ctx);
|
||||
let mut memfd =
|
||||
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
|
||||
memfd.write_all(&data).unwrap();
|
||||
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap();
|
||||
uapi::fcntl_add_seals(
|
||||
memfd.raw(),
|
||||
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
||||
)
|
||||
.unwrap();
|
||||
Ok(Self {
|
||||
id: ids.next(),
|
||||
tranches: vec![DrmFeedbackTranche {
|
||||
device: main_device,
|
||||
indices: (0..index_map.len()).map(|v| v as u16).collect(),
|
||||
scanout: false,
|
||||
}],
|
||||
shared: Rc::new(DrmFeedbackShared {
|
||||
fd: Rc::new(memfd),
|
||||
size: data.len(),
|
||||
main_device,
|
||||
indices: index_map,
|
||||
}),
|
||||
})
|
||||
impl DrmFeedbackContext for dyn GfxContext + '_ {
|
||||
fn main_device(&self) -> Option<uapi::c::dev_t> {
|
||||
self.allocator().drm().map(|drm| drm.dev())
|
||||
}
|
||||
|
||||
pub fn for_scanout(
|
||||
&self,
|
||||
ids: &DrmFeedbackIds,
|
||||
devnum: c::dev_t,
|
||||
formats: &[(u32, Modifier)],
|
||||
) -> Result<Option<Self>, DrmFeedbackError> {
|
||||
let mut tranches = vec![];
|
||||
{
|
||||
let mut indices = vec![];
|
||||
for (format, modifier) in formats {
|
||||
if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) {
|
||||
indices.push(*idx);
|
||||
}
|
||||
}
|
||||
if indices.len() > 0 {
|
||||
tranches.push(DrmFeedbackTranche {
|
||||
device: devnum,
|
||||
indices,
|
||||
scanout: true,
|
||||
});
|
||||
} else {
|
||||
return Ok(None);
|
||||
fn for_each_read_format(&self, f: &mut dyn FnMut(u32, Modifier)) {
|
||||
for (format, info) in &**self.formats() {
|
||||
for modifier in &info.read_modifiers {
|
||||
f(*format, *modifier);
|
||||
}
|
||||
}
|
||||
tranches.extend(self.tranches.iter().cloned());
|
||||
Ok(Some(Self {
|
||||
id: ids.next(),
|
||||
shared: self.shared.clone(),
|
||||
tranches,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn create_fd_data(ctx: &dyn GfxContext) -> (Vec<u8>, AHashMap<(u32, Modifier), u16>) {
|
||||
let mut vec = vec![];
|
||||
let mut map = AHashMap::new();
|
||||
let mut pos = 0;
|
||||
for (format, info) in &**ctx.formats() {
|
||||
for modifier in &info.read_modifiers {
|
||||
vec.write_u32::<NativeEndian>(*format).unwrap();
|
||||
vec.write_u32::<NativeEndian>(0).unwrap();
|
||||
vec.write_u64::<NativeEndian>(*modifier).unwrap();
|
||||
map.insert((*format, *modifier), pos);
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
(vec, map)
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DrmFeedbackError {
|
||||
#[error("Could not stat drm device")]
|
||||
Stat(#[from] OsError),
|
||||
#[error("Graphics API does not have a DRM device")]
|
||||
NoDrmDevice,
|
||||
}
|
||||
|
|
|
|||
1303
src/edid.rs
1303
src/edid.rs
File diff suppressed because it is too large
Load diff
|
|
@ -9,41 +9,13 @@ use {
|
|||
},
|
||||
std::{
|
||||
cmp::Ordering,
|
||||
fmt::{Display, Formatter, LowerHex},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
pub use jay_wire_types::EiObjectId;
|
||||
|
||||
pub const EI_HANDSHAKE_ID: EiHandshakeId = EiHandshakeId::from_raw(0);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct EiObjectId(u64);
|
||||
|
||||
impl EiObjectId {
|
||||
#[expect(dead_code)]
|
||||
pub const NONE: Self = EiObjectId(0);
|
||||
|
||||
pub fn from_raw(raw: u64) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
pub fn raw(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EiObjectId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for EiObjectId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
LowerHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EiObjectBase {
|
||||
fn id(&self) -> EiObjectId;
|
||||
fn version(&self) -> EiVersion;
|
||||
|
|
|
|||
|
|
@ -1,159 +1 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
io_uring::{IoUring, IoUringError},
|
||||
utils::{
|
||||
buf::Buf,
|
||||
errorfmt::ErrorFmt,
|
||||
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
||||
queue::AsyncQueue,
|
||||
stack::Stack,
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, future::poll_fn, pin::Pin, rc::Rc, slice, task::Poll},
|
||||
thiserror::Error,
|
||||
uapi::{OwnedFd, c},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EventfdError {
|
||||
#[error("Could not create an eventfd")]
|
||||
CreateEventfd(#[source] OsError),
|
||||
}
|
||||
|
||||
pub struct EventfdCache {
|
||||
inner: Rc<Inner>,
|
||||
_task: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
ring: Rc<IoUring>,
|
||||
fds: Stack<Rc<OwnedFd>>,
|
||||
recycle: AsyncQueue<Rc<OwnedFd>>,
|
||||
}
|
||||
|
||||
pub struct Eventfd {
|
||||
cache: Rc<Inner>,
|
||||
pub fd: Rc<OwnedFd>,
|
||||
signaled: Cell<bool>,
|
||||
}
|
||||
|
||||
impl EventfdCache {
|
||||
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Rc<Self> {
|
||||
let inner = Rc::new(Inner {
|
||||
ring: ring.clone(),
|
||||
fds: Default::default(),
|
||||
recycle: Default::default(),
|
||||
});
|
||||
let task = eng.spawn("eventfd-cache", inner.clone().recycle());
|
||||
Rc::new(Self { inner, _task: task })
|
||||
}
|
||||
|
||||
pub fn acquire(&self) -> Result<Eventfd, EventfdError> {
|
||||
let fd = match self.inner.fds.pop() {
|
||||
Some(fd) => fd,
|
||||
_ => uapi::eventfd(0, c::EFD_CLOEXEC)
|
||||
.map(Rc::new)
|
||||
.map_os_err(EventfdError::CreateEventfd)?,
|
||||
};
|
||||
Ok(Eventfd {
|
||||
cache: self.inner.clone(),
|
||||
fd,
|
||||
signaled: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Eventfd {
|
||||
pub fn is_signaled(&self) -> bool {
|
||||
self.signaled.get()
|
||||
}
|
||||
|
||||
pub async fn signaled(&self) -> Result<(), IoUringError> {
|
||||
if self.signaled.get() {
|
||||
return Ok(());
|
||||
}
|
||||
self.cache.ring.readable(&self.fd).await?;
|
||||
self.signaled.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn signaled_blocking(&self) -> Result<(), OsError> {
|
||||
if self.signaled.get() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut pollfd = c::pollfd {
|
||||
fd: self.fd.raw(),
|
||||
events: c::POLLIN,
|
||||
revents: 0,
|
||||
};
|
||||
uapi::poll(slice::from_mut(&mut pollfd), -1).to_os_error()?;
|
||||
self.signaled.set(true);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
async fn recycle(self: Rc<Self>) {
|
||||
let slf = &*self;
|
||||
let mut fds = vec![];
|
||||
let mut bufs = vec![];
|
||||
let mut tasks = vec![];
|
||||
let mut todo = vec![];
|
||||
loop {
|
||||
fds.clear();
|
||||
tasks.clear();
|
||||
todo.clear();
|
||||
slf.recycle.non_empty().await;
|
||||
while let Some(fd) = slf.recycle.try_pop() {
|
||||
fds.push(fd);
|
||||
}
|
||||
for (idx, fd) in fds.iter().enumerate() {
|
||||
if idx >= bufs.len() {
|
||||
bufs.push(Buf::new(size_of::<u64>()));
|
||||
}
|
||||
let fd = fd.clone();
|
||||
let buf = bufs[idx].clone();
|
||||
tasks.push(async move { slf.ring.read(&fd, buf).await });
|
||||
todo.push(idx);
|
||||
}
|
||||
poll_fn(|ctx| {
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
let idx = todo[i];
|
||||
let task = unsafe { Pin::new_unchecked(&mut tasks[idx]) };
|
||||
if let Poll::Ready(res) = task.poll(ctx) {
|
||||
todo.swap_remove(i);
|
||||
match res {
|
||||
Ok(_) => {
|
||||
self.fds.push(fds[idx].clone());
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not read from eventfd: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if todo.is_empty() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Eventfd {
|
||||
fn drop(&mut self) {
|
||||
if self.signaled.get() {
|
||||
self.cache.recycle.push(self.fd.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use jay_eventfd_cache::*;
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::AsyncEngine, eventfd_cache::EventfdCache, io_uring::IoUring, utils::array,
|
||||
},
|
||||
std::{rc::Rc, slice},
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let eng = AsyncEngine::new();
|
||||
let ring = IoUring::new(&eng, 32).unwrap();
|
||||
let cache = Rc::new(EventfdCache::new(&ring, &eng));
|
||||
const TOTAL: usize = 5;
|
||||
let signaled = 3;
|
||||
let fd1: [_; TOTAL] = array::from_fn(|_| cache.acquire().unwrap());
|
||||
let fd2: [_; TOTAL] = array::from_fn(|_| cache.acquire().unwrap());
|
||||
for fd in fd1.iter().chain(fd2.iter()) {
|
||||
uapi::eventfd_write(fd.fd.raw(), 1).unwrap();
|
||||
let mut poll = c::pollfd {
|
||||
fd: fd.fd.raw(),
|
||||
events: c::POLLIN,
|
||||
revents: 0,
|
||||
};
|
||||
uapi::poll(slice::from_mut(&mut poll), 0).unwrap();
|
||||
assert_eq!(poll.revents, c::POLLIN);
|
||||
}
|
||||
assert_eq!(cache.inner.fds.len(), 0);
|
||||
let ring2 = ring.clone();
|
||||
let cache2 = cache.clone();
|
||||
let _fut1 = eng.spawn("", async move {
|
||||
for i in 0..signaled {
|
||||
fd1[i].signaled().await.unwrap();
|
||||
}
|
||||
drop(fd1);
|
||||
let debouncer = ring2.debouncer(0);
|
||||
while cache2.inner.fds.len() != signaled {
|
||||
debouncer.debounce().await;
|
||||
}
|
||||
for i in 0..signaled {
|
||||
fd2[i].signaled().await.unwrap();
|
||||
}
|
||||
drop(fd2);
|
||||
while cache2.inner.fds.len() != 2 * signaled {
|
||||
debouncer.debounce().await;
|
||||
}
|
||||
ring2.stop();
|
||||
});
|
||||
let now_nsec = eng.now().nsec();
|
||||
let ring2 = ring.clone();
|
||||
let _fut2 = eng.spawn("", async move {
|
||||
ring2.timeout(now_nsec + 1_000_000_000).await.unwrap();
|
||||
ring2.stop();
|
||||
});
|
||||
ring.run().unwrap();
|
||||
assert_eq!(cache.inner.fds.len(), 2 * signaled);
|
||||
for fd in cache.inner.fds.take() {
|
||||
let mut poll = c::pollfd {
|
||||
fd: fd.raw(),
|
||||
events: c::POLLIN,
|
||||
revents: 0,
|
||||
};
|
||||
uapi::poll(slice::from_mut(&mut poll), 0).unwrap();
|
||||
assert_eq!(poll.revents, 0);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue