1
0
Fork 0
forked from wry/wry

refactor: split cargo workspace

This commit is contained in:
kossLAN 2026-06-05 11:56:21 -04:00
parent 5db14936e7
commit 1c21bd1259
695 changed files with 32023 additions and 44964 deletions

View file

@ -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;

View file

@ -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)))
}
}

View file

@ -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

View file

@ -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::*;

View file

@ -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);
}
}
}
}

View file

@ -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
}
}
}

View file

@ -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>>,
}

View file

@ -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,

View file

@ -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

View 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()
}
}

View 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),
})
}

View 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
}
}

View 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
}
}

View 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()
}
}

View 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),
}
}
}

View file

@ -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::{

View file

@ -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::*;

View file

@ -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),

View file

@ -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
View 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;
}

View file

@ -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)]

View file

@ -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));
}

View file

@ -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);
}
}
}
}

View file

@ -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;

View file

@ -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();

View file

@ -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(),
}
}

View file

@ -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::*;
}

View file

@ -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);
}
}

View file

@ -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]
}

View file

@ -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],
])
}

View file

@ -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
}

View file

@ -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,
}
}
}

View file

@ -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,
}
}
}

View file

@ -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],
],
)
}
}

View file

@ -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),
)
}
}

View file

@ -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,
}
}
}

View file

@ -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, &copy) {
return Err(ConfigError::CopyConfigFile(e));
}
let unlink = UnlinkOnDrop(&copy);
let lib = match unsafe { Library::new(&copy) } {
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

View 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(())
}
}

View 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(())
}
}

View 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);
}
}

View 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(())
}
}

View 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);
}
}

View 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(())
}
}

View 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(())
}
}

View 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
View 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
View 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(())
}
}

View 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(())
})
}
}

View 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());
}
}

View file

@ -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));
&region_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, &copy_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(&regions);
dev.cmd_copy_buffer_to_image2(cmd, &copy);
}
false => {
let copy = CopyImageToBufferInfo2::default()
.src_image(img.img)
.src_image_layout(image_layout)
.dst_buffer(buf.buf)
.regions(&regions);
dev.cmd_copy_image_to_buffer2(cmd, &copy);
}
}
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, &copy_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
View 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));
&region_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, &copy_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(&regions);
dev.cmd_copy_buffer_to_image2(cmd, &copy);
}
false => {
let copy = CopyImageToBufferInfo2::default()
.src_image(img.img)
.src_image_layout(image_layout)
.dst_buffer(buf.buf)
.regions(&regions);
dev.cmd_copy_image_to_buffer2(cmd, &copy);
}
}
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, &copy_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)
}
}

View 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)
}

View 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
}
}
}
}

View file

@ -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::*;

View file

@ -1,2 +0,0 @@
pub mod img_copy;
pub mod read_write;

View file

@ -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
}
}

View file

@ -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)
})
}
}

View file

@ -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);
}

View file

@ -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::*;

View file

@ -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()

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -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)
}
}

View file

@ -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,
},
};

View file

@ -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);
}
}
}

View file

@ -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
}
}

View file

@ -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);
}
}
}

View file

@ -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>;
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -1,4 +0,0 @@
pub mod critm_any_or_all;
pub mod critm_constant;
pub mod critm_exactly;
pub mod critm_string;

View file

@ -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(),
}
}
}

View file

@ -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
}
}

View file

@ -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(),
}
}
}

View file

@ -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))
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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),

View file

@ -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 _,
}
}
}

View file

@ -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,
};
}

View file

@ -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)
}
}

View file

@ -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);
}
}
}
}
}

View file

@ -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(

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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()
}
}

View file

@ -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,

View file

@ -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,
}
}
}

View file

@ -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,
}

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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::*;

View file

@ -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