autocommit 2022-03-30 03:00:46 CEST
This commit is contained in:
parent
9842264fad
commit
28c9b46400
40 changed files with 1212 additions and 175 deletions
102
Cargo.lock
generated
102
Cargo.lock
generated
|
|
@ -28,15 +28,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.56"
|
||||
|
|
@ -132,19 +123,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.6"
|
||||
|
|
@ -163,6 +141,15 @@ dependencies = [
|
|||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.1.4"
|
||||
|
|
@ -186,16 +173,23 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.0"
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -306,11 +300,12 @@ dependencies = [
|
|||
"bitflags",
|
||||
"bstr",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"default-config",
|
||||
"env_logger",
|
||||
"dirs",
|
||||
"futures-util",
|
||||
"humantime",
|
||||
"isnt",
|
||||
"jay-config",
|
||||
"libloading",
|
||||
|
|
@ -392,16 +387,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
|
|
@ -545,14 +530,32 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
|
|
@ -660,17 +663,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uapi"
|
||||
version = "0.2.7"
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ panic = "abort"
|
|||
uapi = "0.2.7"
|
||||
thiserror = "1.0.30"
|
||||
ahash = "0.7.6"
|
||||
log = "0.4.14"
|
||||
env_logger = "0.9.0"
|
||||
log = { version = "0.4.16", features = ["std"] }
|
||||
futures-util = "0.3.19"
|
||||
num-traits = "0.2.14"
|
||||
num-derive = "0.3.3"
|
||||
|
|
@ -35,9 +34,11 @@ jay-config = { path = "jay-config" }
|
|||
default-config = { path = "default-config" }
|
||||
pin-project = "1.0.10"
|
||||
clap = { version = "3.1.6", features = ["derive", "wrap_help"] }
|
||||
clap_complete = "3.1.1"
|
||||
humantime = "2.1.0"
|
||||
dirs = "4.0.0"
|
||||
|
||||
backtrace = { version = "0.3.64", optional = true }
|
||||
chrono = { version = "0.4.19", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
repc = "0.1.1"
|
||||
|
|
@ -48,4 +49,4 @@ bstr = { version = "0.2.17", default-features = false, features = ["std"] }
|
|||
#opt-level = 3
|
||||
|
||||
[features]
|
||||
rc_tracking = ["backtrace", "chrono"]
|
||||
rc_tracking = ["backtrace"]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
clippy::unnecessary_to_owned,
|
||||
clippy::match_like_matches_macro,
|
||||
clippy::too_many_arguments,
|
||||
clippy::iter_skip_next,
|
||||
clippy::iter_skip_next
|
||||
)]
|
||||
|
||||
extern crate core;
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ fn write_message<W: Write>(f: &mut W, obj: &BStr, message: &Message) -> Result<(
|
|||
writeln!(f, " pub const {}: u32 = {};", uppercase, message.id.val)?;
|
||||
write_message_type(f, obj, message, has_reference_type)?;
|
||||
let lifetime = if has_reference_type { "<'a>" } else { "" };
|
||||
let lifetime_b = if has_reference_type { "<'b>" } else { "" };
|
||||
let parser = if message.fields.len() > 0 {
|
||||
"parser"
|
||||
} else {
|
||||
|
|
@ -600,6 +601,12 @@ fn write_message<W: Write>(f: &mut W, obj: &BStr, message: &Message) -> Result<(
|
|||
" impl<'a> RequestParser<'a> for {}{} {{",
|
||||
message.camel_name, lifetime
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
" type Generic<'b> = {}{};",
|
||||
message.camel_name, lifetime_b,
|
||||
)?;
|
||||
writeln!(f, " const ID: u32 = {};", message.id.val,)?;
|
||||
writeln!(
|
||||
f,
|
||||
" fn parse({}: &mut MsgParser<'_, 'a>) -> Result<Self, MsgParserError> {{",
|
||||
|
|
|
|||
109
src/acceptor.rs
109
src/acceptor.rs
|
|
@ -37,7 +37,7 @@ pub enum AcceptorError {
|
|||
}
|
||||
|
||||
pub struct Acceptor {
|
||||
id: EventLoopId,
|
||||
ids: [EventLoopId; 2],
|
||||
socket: AllocatedSocket,
|
||||
global: Rc<State>,
|
||||
}
|
||||
|
|
@ -47,10 +47,12 @@ struct AllocatedSocket {
|
|||
name: Ustring,
|
||||
// /run/user/1000/wayland-x
|
||||
path: Ustring,
|
||||
fd: Rc<OwnedFd>,
|
||||
insecure: Rc<OwnedFd>,
|
||||
// /run/user/1000/wayland-x.lock
|
||||
lock_path: Ustring,
|
||||
_lock_fd: OwnedFd,
|
||||
// /run/user/1000/wayland-x.jay
|
||||
secure: Rc<OwnedFd>,
|
||||
}
|
||||
|
||||
impl Drop for AllocatedSocket {
|
||||
|
|
@ -60,13 +62,19 @@ impl Drop for AllocatedSocket {
|
|||
}
|
||||
}
|
||||
|
||||
fn bind_socket(fd: &Rc<OwnedFd>, xrd: &str, id: u32) -> Result<AllocatedSocket, AcceptorError> {
|
||||
fn bind_socket(
|
||||
insecure: &Rc<OwnedFd>,
|
||||
secure: &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_ustr!("wayland-{}", id);
|
||||
let path = format_ustr!("{}/{}", xrd, name.display());
|
||||
let jay_path = format_ustr!("{}.jay", path.display());
|
||||
let lock_path = format_ustr!("{}.lock", path.display());
|
||||
if path.len() + 1 > addr.sun_path.len() {
|
||||
if jay_path.len() + 1 > addr.sun_path.len() {
|
||||
return Err(AcceptorError::XrdTooLong(xrd.to_string()));
|
||||
}
|
||||
let lock_fd = match uapi::open(&*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_RDWR, 0o644) {
|
||||
|
|
@ -76,26 +84,29 @@ fn bind_socket(fd: &Rc<OwnedFd>, xrd: &str, id: u32) -> Result<AllocatedSocket,
|
|||
if let Err(e) = uapi::flock(lock_fd.raw(), c::LOCK_EX | c::LOCK_NB) {
|
||||
return Err(AcceptorError::LockLockFile(e.into()));
|
||||
}
|
||||
match uapi::lstat(&*path) {
|
||||
Ok(_) => {
|
||||
log::info!("Unlinking {}", path.display());
|
||||
let _ = uapi::unlink(&*path);
|
||||
for (name, fd) in [(&path, insecure), (&jay_path, secure)] {
|
||||
match uapi::lstat(name) {
|
||||
Ok(_) => {
|
||||
log::info!("Unlinking {}", name.display());
|
||||
let _ = uapi::unlink(name);
|
||||
}
|
||||
Err(Errno(c::ENOENT)) => {}
|
||||
Err(e) => return Err(AcceptorError::SocketStat(e.into())),
|
||||
}
|
||||
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;
|
||||
if let Err(e) = uapi::bind(fd.raw(), &addr) {
|
||||
return Err(AcceptorError::BindFailed(e.into()));
|
||||
}
|
||||
Err(Errno(c::ENOENT)) => {}
|
||||
Err(e) => return Err(AcceptorError::SocketStat(e.into())),
|
||||
}
|
||||
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;
|
||||
if let Err(e) = uapi::bind(fd.raw(), &addr) {
|
||||
return Err(AcceptorError::BindFailed(e.into()));
|
||||
}
|
||||
Ok(AllocatedSocket {
|
||||
name,
|
||||
path,
|
||||
fd: fd.clone(),
|
||||
insecure: insecure.clone(),
|
||||
lock_path,
|
||||
_lock_fd: lock_fd,
|
||||
secure: secure.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -104,16 +115,22 @@ fn allocate_socket() -> Result<AllocatedSocket, AcceptorError> {
|
|||
Ok(d) => d,
|
||||
Err(_) => return Err(AcceptorError::XrdNotSet),
|
||||
};
|
||||
let fd = match uapi::socket(
|
||||
c::AF_UNIX,
|
||||
c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
|
||||
0,
|
||||
) {
|
||||
Ok(f) => Rc::new(f),
|
||||
Err(e) => return Err(AcceptorError::SocketFailed(e.into())),
|
||||
};
|
||||
let mut fds = [None, None];
|
||||
for fd in &mut fds {
|
||||
let socket = match uapi::socket(
|
||||
c::AF_UNIX,
|
||||
c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
|
||||
0,
|
||||
) {
|
||||
Ok(f) => Rc::new(f),
|
||||
Err(e) => return Err(AcceptorError::SocketFailed(e.into())),
|
||||
};
|
||||
*fd = Some(socket);
|
||||
}
|
||||
let unsecure = fds[0].take().unwrap();
|
||||
let secure = fds[1].take().unwrap();
|
||||
for i in 1..1000 {
|
||||
match bind_socket(&fd, &xrd, i) {
|
||||
match bind_socket(&unsecure, &secure, &xrd, i) {
|
||||
Ok(s) => return Ok(s),
|
||||
Err(e) => {
|
||||
log::warn!("Cannot use the wayland-{} socket: {}", i, ErrorFmt(e));
|
||||
|
|
@ -124,34 +141,49 @@ fn allocate_socket() -> Result<AllocatedSocket, AcceptorError> {
|
|||
}
|
||||
|
||||
impl Acceptor {
|
||||
pub fn install(global: &Rc<State>) -> Result<Ustring, AcceptorError> {
|
||||
pub fn install(state: &Rc<State>) -> Result<Ustring, AcceptorError> {
|
||||
let socket = allocate_socket()?;
|
||||
log::info!("bound to socket {}", socket.path.display());
|
||||
if let Err(e) = uapi::listen(socket.fd.raw(), 4096) {
|
||||
return Err(AcceptorError::ListenFailed(e.into()));
|
||||
for fd in [&socket.secure, &socket.insecure] {
|
||||
if let Err(e) = uapi::listen(fd.raw(), 4096) {
|
||||
return Err(AcceptorError::ListenFailed(e.into()));
|
||||
}
|
||||
}
|
||||
let id = global.el.id();
|
||||
let id1 = state.el.id();
|
||||
let id2 = state.el.id();
|
||||
let name = socket.name.to_owned();
|
||||
let acc = Rc::new(Acceptor {
|
||||
id,
|
||||
ids: [id1, id2],
|
||||
socket,
|
||||
global: global.clone(),
|
||||
global: state.clone(),
|
||||
});
|
||||
global
|
||||
state.el.insert(
|
||||
id1,
|
||||
Some(acc.socket.insecure.raw()),
|
||||
c::EPOLLIN,
|
||||
acc.clone(),
|
||||
)?;
|
||||
state
|
||||
.el
|
||||
.insert(id, Some(acc.socket.fd.raw()), c::EPOLLIN, acc)?;
|
||||
.insert(id2, Some(acc.socket.secure.raw()), c::EPOLLIN, acc)?;
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopDispatcher for Acceptor {
|
||||
fn dispatch(self: Rc<Self>, events: i32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn dispatch(
|
||||
self: Rc<Self>,
|
||||
fd: Option<i32>,
|
||||
events: i32,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
|
||||
return Err(Box::new(AcceptorError::ErrorEvent));
|
||||
}
|
||||
let fd = fd.unwrap();
|
||||
let secure = fd == self.socket.secure.raw();
|
||||
loop {
|
||||
let fd = match uapi::accept4(
|
||||
self.socket.fd.raw(),
|
||||
fd,
|
||||
uapi::sockaddr_none_mut(),
|
||||
c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
|
||||
) {
|
||||
|
|
@ -160,7 +192,7 @@ impl EventLoopDispatcher for Acceptor {
|
|||
Err(e) => return Err(Box::new(AcceptorError::AcceptFailed(e.into()))),
|
||||
};
|
||||
let id = self.global.clients.id();
|
||||
if let Err(e) = self.global.clients.spawn(id, &self.global, fd) {
|
||||
if let Err(e) = self.global.clients.spawn(id, &self.global, fd, secure) {
|
||||
return Err(Box::new(AcceptorError::SpawnFailed(e)));
|
||||
}
|
||||
}
|
||||
|
|
@ -170,6 +202,7 @@ impl EventLoopDispatcher for Acceptor {
|
|||
|
||||
impl Drop for Acceptor {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.global.el.remove(self.id);
|
||||
let _ = self.global.el.remove(self.ids[0]);
|
||||
let _ = self.global.el.remove(self.ids[1]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ mod queue {
|
|||
}
|
||||
|
||||
impl EventLoopDispatcher for Dispatcher {
|
||||
fn dispatch(self: Rc<Self>, _events: i32) -> Result<(), Box<dyn Error>> {
|
||||
fn dispatch(self: Rc<Self>, _fd: Option<i32>, _events: i32) -> Result<(), Box<dyn Error>> {
|
||||
let mut stash = self.stash.borrow_mut();
|
||||
let mut yield_stash = self.yield_stash.borrow_mut();
|
||||
while self.queue.num_queued.get() > 0 {
|
||||
|
|
@ -609,7 +609,7 @@ mod fd {
|
|||
}
|
||||
|
||||
impl EventLoopDispatcher for AsyncFdData {
|
||||
fn dispatch(self: Rc<Self>, events: i32) -> Result<(), Box<dyn Error>> {
|
||||
fn dispatch(self: Rc<Self>, _fd: Option<i32>, events: i32) -> Result<(), Box<dyn Error>> {
|
||||
let mut status = FdStatus::Ok;
|
||||
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
|
||||
status = FdStatus::Err;
|
||||
|
|
|
|||
|
|
@ -607,14 +607,19 @@ impl XBackendData {
|
|||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let mut matched_any = false;
|
||||
for image in &output.images {
|
||||
if image.last_serial.get() == event.serial {
|
||||
matched_any = true;
|
||||
image.idle.set(true);
|
||||
if image.render_on_idle.replace(false) {
|
||||
self.schedule_present(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !matched_any {
|
||||
fatal!("idle event did not match any images {:#?}", event);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
108
src/cli.rs
108
src/cli.rs
|
|
@ -1,40 +1,108 @@
|
|||
use clap::{ArgEnum, Args, Parser, Subcommand};
|
||||
mod generate;
|
||||
mod log;
|
||||
|
||||
use crate::compositor::start_compositor;
|
||||
use ::log::Level;
|
||||
use clap::{ArgEnum, Args, Parser, Subcommand};
|
||||
use clap_complete::Shell;
|
||||
|
||||
/// A wayland compositor.
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Cli {
|
||||
struct Jay {
|
||||
#[clap(flatten)]
|
||||
pub global: GlobalArgs,
|
||||
global: GlobalArgs,
|
||||
#[clap(subcommand)]
|
||||
pub command: Cmd,
|
||||
command: Cmd,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct GlobalArgs {
|
||||
#[clap(long)]
|
||||
hurr: String,
|
||||
/// The log level.
|
||||
#[clap(arg_enum, long, default_value_t)]
|
||||
pub log_level: CliLogLevel,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Cmd {
|
||||
/// Run the compositor
|
||||
Run,
|
||||
Test(Test),
|
||||
/// Run the compositor.
|
||||
Run(RunArgs),
|
||||
/// Generate shell completion scripts for jay.
|
||||
GenerateCompletion(GenerateArgs),
|
||||
/// Open the log file.
|
||||
Log(LogArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct Test {
|
||||
/// a
|
||||
pub struct RunArgs {
|
||||
/// The backends to try.
|
||||
///
|
||||
/// b
|
||||
/// By default, jay will try to start the available backends in this order: x11,metal.
|
||||
/// The first backend that can be started will be used.
|
||||
///
|
||||
/// c
|
||||
#[clap(long, use_value_delimiter = true, arg_enum)]
|
||||
shell: Vec<Hurr>,
|
||||
/// Using this option, you can change which backends will be tried and change the order in
|
||||
/// which they will be tried. Multiple backends can be supplied as a comma-separated list.
|
||||
#[clap(arg_enum, use_value_delimiter = true, long)]
|
||||
pub backends: Vec<CliBackend>,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Debug, Copy, Clone)]
|
||||
pub enum Hurr {
|
||||
Bash,
|
||||
Fish,
|
||||
Zsh,
|
||||
#[derive(Args, Debug)]
|
||||
pub struct LogArgs {
|
||||
/// Print the path of the log file.
|
||||
#[clap(long)]
|
||||
path: bool,
|
||||
/// Follow the log.
|
||||
#[clap(long, short)]
|
||||
follow: bool,
|
||||
/// Immediately jump to the end in the pager.
|
||||
#[clap(long, short = 'e')]
|
||||
pager_end: bool,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Debug, Copy, Clone, Hash)]
|
||||
pub enum CliBackend {
|
||||
X11,
|
||||
Metal,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Debug, Copy, Clone, Hash)]
|
||||
pub enum CliLogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl Into<Level> for CliLogLevel {
|
||||
fn into(self) -> Level {
|
||||
match self {
|
||||
CliLogLevel::Trace => Level::Trace,
|
||||
CliLogLevel::Debug => Level::Debug,
|
||||
CliLogLevel::Info => Level::Info,
|
||||
CliLogLevel::Warn => Level::Warn,
|
||||
CliLogLevel::Error => Level::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CliLogLevel {
|
||||
fn default() -> Self {
|
||||
Self::Info
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct GenerateArgs {
|
||||
/// The shell to generate completions for
|
||||
#[clap(arg_enum)]
|
||||
shell: Shell,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let cli = Jay::parse();
|
||||
match cli.command {
|
||||
Cmd::Run(a) => start_compositor(cli.global, a),
|
||||
Cmd::GenerateCompletion(g) => generate::main(g),
|
||||
Cmd::Log(a) => log::main(cli.global, a),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
src/cli/generate.rs
Normal file
9
src/cli/generate.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use crate::cli::{GenerateArgs, Jay};
|
||||
use clap::CommandFactory;
|
||||
use std::io::stdout;
|
||||
|
||||
pub fn main(args: GenerateArgs) {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
clap_complete::generate(args.shell, &mut Jay::command(), "jay", &mut stdout);
|
||||
}
|
||||
99
src/cli/log.rs
Normal file
99
src/cli/log.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
use crate::cli::{GlobalArgs, LogArgs};
|
||||
use crate::object::WL_DISPLAY_ID;
|
||||
use crate::tools::tool_client::{Handle, ToolClient};
|
||||
use crate::utils::errorfmt::ErrorFmt;
|
||||
use crate::wire::wl_display::GetRegistry;
|
||||
use crate::wire::{
|
||||
jay_compositor, jay_log_file, wl_registry, JayCompositor, JayCompositorId, WlRegistryId,
|
||||
};
|
||||
use bstr::{BString, ByteSlice};
|
||||
use jay_compositor::GetLogFile;
|
||||
use jay_log_file::Path;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use wl_registry::{Bind, Global};
|
||||
|
||||
pub fn main(global: GlobalArgs, args: LogArgs) {
|
||||
let tc = ToolClient::new(global.log_level.into());
|
||||
let logger = Rc::new(Log {
|
||||
tc: tc.clone(),
|
||||
registry: Cell::new(WlRegistryId::NONE),
|
||||
comp: Cell::new(JayCompositorId::NONE),
|
||||
path: RefCell::new(None),
|
||||
args,
|
||||
});
|
||||
tc.run(run(logger));
|
||||
}
|
||||
|
||||
struct Log {
|
||||
tc: Rc<ToolClient>,
|
||||
registry: Cell<WlRegistryId>,
|
||||
comp: Cell<JayCompositorId>,
|
||||
path: RefCell<Option<BString>>,
|
||||
args: LogArgs,
|
||||
}
|
||||
|
||||
async fn run(log: Rc<Log>) {
|
||||
let tc = &log.tc;
|
||||
let registry = tc.id();
|
||||
tc.send(GetRegistry {
|
||||
self_id: WL_DISPLAY_ID,
|
||||
registry,
|
||||
});
|
||||
log.registry.set(registry);
|
||||
Global::handle(tc, registry, log.clone(), |log, g| {
|
||||
if g.interface == JayCompositor.name() {
|
||||
let id: JayCompositorId = log.tc.id();
|
||||
log.tc.send(Bind {
|
||||
self_id: log.registry.get(),
|
||||
name: g.name,
|
||||
interface: g.interface,
|
||||
version: 1,
|
||||
id: id.into(),
|
||||
});
|
||||
log.comp.set(id);
|
||||
}
|
||||
});
|
||||
tc.round_trip().await;
|
||||
let comp = log.comp.get();
|
||||
if comp.is_none() {
|
||||
fatal!(
|
||||
"Server does not provide the {} interface",
|
||||
JayCompositor.name()
|
||||
);
|
||||
}
|
||||
let log_file = tc.id();
|
||||
tc.send(GetLogFile {
|
||||
self_id: comp,
|
||||
id: log_file,
|
||||
});
|
||||
Path::handle(tc, log_file, log.clone(), |log, path| {
|
||||
*log.path.borrow_mut() = Some(path.path.to_vec().into());
|
||||
});
|
||||
tc.round_trip().await;
|
||||
let path = log.path.borrow_mut();
|
||||
let path = match path.deref() {
|
||||
Some(p) => p,
|
||||
_ => fatal!("Server did not send the path of the log file"),
|
||||
};
|
||||
if log.args.path {
|
||||
println!("{}", path);
|
||||
process::exit(0);
|
||||
}
|
||||
let mut command = Command::new("less");
|
||||
if log.args.pager_end {
|
||||
command.arg("+G");
|
||||
}
|
||||
if log.args.follow {
|
||||
command.arg("+F");
|
||||
} else {
|
||||
command.arg("-S");
|
||||
}
|
||||
command.arg(path.as_bytes().to_os_str().unwrap());
|
||||
let err = command.exec();
|
||||
fatal!("Could not spawn `less`: {}", ErrorFmt(err));
|
||||
}
|
||||
|
|
@ -75,6 +75,7 @@ impl Clients {
|
|||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: OwnedFd,
|
||||
secure: bool,
|
||||
) -> Result<(), ClientError> {
|
||||
let (uid, pid) = {
|
||||
let mut cred = c::ucred {
|
||||
|
|
@ -93,7 +94,7 @@ impl Clients {
|
|||
}
|
||||
}
|
||||
};
|
||||
self.spawn2(id, global, socket, uid, pid, None)?;
|
||||
self.spawn2(id, global, socket, uid, pid, secure, None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +105,7 @@ impl Clients {
|
|||
socket: OwnedFd,
|
||||
uid: c::uid_t,
|
||||
pid: c::pid_t,
|
||||
secure: bool,
|
||||
xwayland_queue: Option<Rc<AsyncQueue<XWaylandEvent>>>,
|
||||
) -> Result<Rc<Client>, ClientError> {
|
||||
let data = Rc::new(Client {
|
||||
|
|
@ -118,6 +120,7 @@ impl Clients {
|
|||
dispatch_frame_requests: AsyncQueue::new(),
|
||||
tracker: Default::default(),
|
||||
xwayland_queue,
|
||||
secure,
|
||||
});
|
||||
track!(data, data);
|
||||
let display = Rc::new(WlDisplay::new(&data));
|
||||
|
|
@ -129,11 +132,12 @@ impl Clients {
|
|||
data: data.clone(),
|
||||
};
|
||||
log::info!(
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}",
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, secure: {}",
|
||||
id,
|
||||
pid,
|
||||
uid,
|
||||
client.data.socket.raw()
|
||||
client.data.socket.raw(),
|
||||
secure,
|
||||
);
|
||||
self.clients.borrow_mut().insert(client.data.id, client);
|
||||
Ok(data)
|
||||
|
|
@ -155,13 +159,15 @@ impl Clients {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn broadcast<B>(&self, mut f: B)
|
||||
pub fn broadcast<B>(&self, secure: bool, mut f: B)
|
||||
where
|
||||
B: FnMut(&Rc<Client>),
|
||||
{
|
||||
let clients = self.clients.borrow();
|
||||
for client in clients.values() {
|
||||
f(&client.data);
|
||||
if !secure || client.data.secure {
|
||||
f(&client.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +200,8 @@ pub trait EventFormatter: Debug {
|
|||
}
|
||||
|
||||
pub trait RequestParser<'a>: Debug + Sized {
|
||||
type Generic<'b>: RequestParser<'b>;
|
||||
const ID: u32;
|
||||
fn parse(parser: &mut MsgParser<'_, 'a>) -> Result<Self, MsgParserError>;
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +217,7 @@ pub struct Client {
|
|||
pub dispatch_frame_requests: AsyncQueue<Rc<WlCallback>>,
|
||||
pub tracker: Tracker<Client>,
|
||||
pub xwayland_queue: Option<Rc<AsyncQueue<XWaylandEvent>>>,
|
||||
pub secure: bool,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::acceptor::{Acceptor, AcceptorError};
|
||||
use crate::async_engine::{AsyncEngine, AsyncError, Phase};
|
||||
use crate::cli::{GlobalArgs, RunArgs};
|
||||
use crate::client::Clients;
|
||||
use crate::clientmem::ClientMemError;
|
||||
use crate::config::ConfigProxy;
|
||||
|
|
@ -8,6 +9,7 @@ use crate::event_loop::{EventLoop, EventLoopError};
|
|||
use crate::forker::ForkerError;
|
||||
use crate::globals::Globals;
|
||||
use crate::ifs::wl_surface::NoneSurfaceExt;
|
||||
use crate::logger::Logger;
|
||||
use crate::render::RenderError;
|
||||
use crate::sighand::SighandError;
|
||||
use crate::state::State;
|
||||
|
|
@ -22,21 +24,20 @@ use crate::utils::run_toplevel::RunToplevel;
|
|||
use crate::wheel::{Wheel, WheelError};
|
||||
use crate::xkbcommon::XkbContext;
|
||||
use crate::{clientmem, forker, leaks, render, sighand, tasks, xwayland};
|
||||
use log::LevelFilter;
|
||||
use forker::ForkerProxy;
|
||||
use std::cell::Cell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
pub fn start_compositor() {
|
||||
env_logger::builder()
|
||||
.format_timestamp_millis()
|
||||
.filter_level(LevelFilter::Info)
|
||||
.filter_level(LevelFilter::Debug)
|
||||
// .filter_level(LevelFilter::Trace)
|
||||
.init();
|
||||
if let Err(e) = main_() {
|
||||
log::error!("A fatal error occurred: {}", ErrorFmt(e));
|
||||
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||
let logger = Logger::install_compositor(global.log_level.into());
|
||||
if let Err(e) = main_(logger.clone(), &args) {
|
||||
let e = ErrorFmt(e);
|
||||
log::error!("A fatal error occurred: {}", e);
|
||||
eprintln!("A fatal error occurred: {}", e);
|
||||
eprintln!("See {} for more details.", logger.path());
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,8 +62,8 @@ enum MainError {
|
|||
ForkerError(#[from] ForkerError),
|
||||
}
|
||||
|
||||
fn main_() -> Result<(), MainError> {
|
||||
let forker = Rc::new(forker::ForkerProxy::create()?);
|
||||
fn main_(logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
|
||||
let forker = Rc::new(ForkerProxy::create()?);
|
||||
leaks::init();
|
||||
render::init()?;
|
||||
clientmem::init()?;
|
||||
|
|
@ -108,6 +109,7 @@ fn main_() -> Result<(), MainError> {
|
|||
pending_float_titles: Default::default(),
|
||||
dbus: Dbus::new(&engine, &run_toplevel),
|
||||
fdcloser: FdCloser::new(),
|
||||
logger,
|
||||
});
|
||||
forker.install(&state);
|
||||
let config = ConfigProxy::default(&state);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@ pub enum EventLoopError {
|
|||
pub struct EventLoopId(u64);
|
||||
|
||||
pub trait EventLoopDispatcher {
|
||||
fn dispatch(self: Rc<Self>, events: i32) -> Result<(), Box<dyn std::error::Error>>;
|
||||
fn dispatch(
|
||||
self: Rc<Self>,
|
||||
fd: Option<i32>,
|
||||
events: i32,
|
||||
) -> Result<(), Box<dyn std::error::Error>>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -157,7 +161,7 @@ impl EventLoop {
|
|||
break 'outer;
|
||||
}
|
||||
if let Some(entry) = self.entries.get(&id) {
|
||||
if let Err(e) = entry.dispatcher.clone().dispatch(0) {
|
||||
if let Err(e) = entry.dispatcher.clone().dispatch(entry.fd, 0) {
|
||||
return Err(EventLoopError::DispatcherError(e));
|
||||
}
|
||||
}
|
||||
|
|
@ -185,7 +189,11 @@ impl EventLoop {
|
|||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(e) = entry.dispatcher.clone().dispatch(event.events as i32) {
|
||||
if let Err(e) = entry
|
||||
.dispatcher
|
||||
.clone()
|
||||
.dispatch(entry.fd, event.events as i32)
|
||||
{
|
||||
return Err(EventLoopError::DispatcherError(e));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::client::Client;
|
||||
use crate::ifs::ipc::wl_data_device_manager::WlDataDeviceManagerGlobal;
|
||||
use crate::ifs::ipc::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global;
|
||||
use crate::ifs::jay_compositor::JayCompositorGlobal;
|
||||
use crate::ifs::org_kde_kwin_server_decoration_manager::OrgKdeKwinServerDecorationManagerGlobal;
|
||||
use crate::ifs::wl_compositor::WlCompositorGlobal;
|
||||
use crate::ifs::wl_drm::WlDrmGlobal;
|
||||
|
|
@ -77,6 +78,9 @@ pub trait Global: GlobalBase {
|
|||
fn singleton(&self) -> bool;
|
||||
fn version(&self) -> u32;
|
||||
fn break_loops(&self) {}
|
||||
fn secure(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Globals {
|
||||
|
|
@ -111,6 +115,7 @@ impl Globals {
|
|||
add_singleton!(ZwpPrimarySelectionDeviceManagerV1Global);
|
||||
add_singleton!(ZwlrLayerShellV1Global);
|
||||
add_singleton!(ZxdgOutputManagerV1Global);
|
||||
add_singleton!(JayCompositorGlobal);
|
||||
slf
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +137,7 @@ impl Globals {
|
|||
|
||||
fn insert(&self, state: &State, global: Rc<dyn Global>) {
|
||||
self.insert_no_broadcast_(&global);
|
||||
self.broadcast(state, |r| r.send_global(&global));
|
||||
self.broadcast(state, global.secure(), |r| r.send_global(&global));
|
||||
}
|
||||
|
||||
pub fn get(&self, name: GlobalName) -> Result<Rc<dyn Global>, GlobalsError> {
|
||||
|
|
@ -142,7 +147,9 @@ impl Globals {
|
|||
pub fn remove<T: WaylandGlobal>(&self, state: &State, global: &T) -> Result<(), GlobalsError> {
|
||||
let _global = self.take(global.name(), true)?;
|
||||
global.remove(self);
|
||||
self.broadcast(state, |r| r.send_global_remove(global.name()));
|
||||
self.broadcast(state, global.secure(), |r| {
|
||||
r.send_global_remove(global.name())
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -151,12 +158,15 @@ impl Globals {
|
|||
}
|
||||
|
||||
pub fn notify_all(&self, registry: &Rc<WlRegistry>) {
|
||||
let secure = registry.client.secure;
|
||||
let globals = self.registry.lock();
|
||||
macro_rules! emit {
|
||||
($singleton:expr) => {
|
||||
for global in globals.values() {
|
||||
if global.singleton() == $singleton {
|
||||
registry.send_global(global);
|
||||
if secure || !global.secure() {
|
||||
if global.singleton() == $singleton {
|
||||
registry.send_global(global);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -165,8 +175,8 @@ impl Globals {
|
|||
emit!(false);
|
||||
}
|
||||
|
||||
fn broadcast<F: Fn(&Rc<WlRegistry>)>(&self, state: &State, f: F) {
|
||||
state.clients.broadcast(|c| {
|
||||
fn broadcast<F: Fn(&Rc<WlRegistry>)>(&self, state: &State, secure: bool, f: F) {
|
||||
state.clients.broadcast(secure, |c| {
|
||||
let registries = c.lock_registries();
|
||||
for registry in registries.values() {
|
||||
f(registry);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
pub mod ipc;
|
||||
pub mod jay_compositor;
|
||||
pub mod jay_log_file;
|
||||
pub mod org_kde_kwin_server_decoration;
|
||||
pub mod org_kde_kwin_server_decoration_manager;
|
||||
pub mod wl_buffer;
|
||||
|
|
|
|||
123
src/ifs/jay_compositor.rs
Normal file
123
src/ifs/jay_compositor.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use crate::client::{Client, ClientError};
|
||||
use crate::globals::{Global, GlobalName};
|
||||
use crate::ifs::jay_log_file::JayLogFile;
|
||||
use crate::leaks::Tracker;
|
||||
use crate::object::Object;
|
||||
use crate::utils::buffd::{MsgParser, MsgParserError};
|
||||
use crate::wire::jay_compositor::*;
|
||||
use crate::wire::JayCompositorId;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
|
||||
pub struct JayCompositorGlobal {
|
||||
name: GlobalName,
|
||||
}
|
||||
|
||||
impl JayCompositorGlobal {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: JayCompositorId,
|
||||
client: &Rc<Client>,
|
||||
_version: u32,
|
||||
) -> Result<(), JayCompositorError> {
|
||||
let obj = Rc::new(JayCompositor {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError);
|
||||
|
||||
impl Global for JayCompositorGlobal {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(JayCompositorGlobal);
|
||||
|
||||
pub struct JayCompositor {
|
||||
id: JayCompositorId,
|
||||
client: Rc<Client>,
|
||||
tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl JayCompositor {
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_log_file(&self, parser: MsgParser<'_, '_>) -> Result<(), GetLogFileError> {
|
||||
let req: GetLogFile = self.client.parse(self, parser)?;
|
||||
let log_file = Rc::new(JayLogFile::new(req.id, &self.client));
|
||||
track!(self.client, log_file);
|
||||
self.client.add_client_obj(&log_file)?;
|
||||
log_file.send_path(self.client.state.logger.path());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
JayCompositor, JayCompositorError;
|
||||
|
||||
DESTROY => destroy,
|
||||
GET_LOG_FILE => get_log_file,
|
||||
}
|
||||
|
||||
impl Object for JayCompositor {
|
||||
fn num_requests(&self) -> u32 {
|
||||
GET_LOG_FILE + 1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(JayCompositor);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayCompositorError {
|
||||
#[error("Could not process a `destroy` request")]
|
||||
DestroyError(#[from] DestroyError),
|
||||
#[error("Could not process a `get_log_file` request")]
|
||||
GetLogFileError(#[from] GetLogFileError),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(JayCompositorError, ClientError);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DestroyError {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(DestroyError, ClientError);
|
||||
efrom!(DestroyError, MsgParserError);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GetLogFileError {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(GetLogFileError, ClientError);
|
||||
efrom!(GetLogFileError, MsgParserError);
|
||||
68
src/ifs/jay_log_file.rs
Normal file
68
src/ifs/jay_log_file.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use crate::client::{Client, ClientError};
|
||||
use crate::leaks::Tracker;
|
||||
use crate::object::Object;
|
||||
use crate::utils::buffd::{MsgParser, MsgParserError};
|
||||
use crate::wire::jay_log_file::*;
|
||||
use crate::wire::JayLogFileId;
|
||||
use bstr::BStr;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
|
||||
pub struct JayLogFile {
|
||||
pub id: JayLogFileId,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl JayLogFile {
|
||||
pub fn new(id: JayLogFileId, client: &Rc<Client>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_path(&self, path: &BStr) {
|
||||
self.client.event(Path {
|
||||
self_id: self.id,
|
||||
path,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
JayLogFile, JayLogFileError;
|
||||
|
||||
DESTROY => destroy,
|
||||
}
|
||||
|
||||
impl Object for JayLogFile {
|
||||
fn num_requests(&self) -> u32 {
|
||||
DESTROY + 1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(JayLogFile);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayLogFileError {
|
||||
#[error("Could not process a `destroy` request")]
|
||||
DestroyError(#[from] DestroyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DestroyError {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(DestroyError, ClientError);
|
||||
efrom!(DestroyError, MsgParserError);
|
||||
|
|
@ -11,7 +11,7 @@ use thiserror::Error;
|
|||
|
||||
pub struct WlRegistry {
|
||||
id: WlRegistryId,
|
||||
client: Rc<Client>,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ use crate::utils::copyhashmap::CopyHashMap;
|
|||
use crate::utils::errorfmt::ErrorFmt;
|
||||
use crate::utils::linkedlist::{LinkedList, LinkedNode};
|
||||
use crate::utils::numcell::NumCell;
|
||||
use crate::utils::rc_eq::rc_eq;
|
||||
use crate::wire::wl_seat::*;
|
||||
use crate::wire::{
|
||||
WlDataDeviceId, WlKeyboardId, WlPointerId, WlSeatId, ZwpPrimarySelectionDeviceV1Id,
|
||||
|
|
@ -52,7 +53,6 @@ use std::ops::{Deref, DerefMut};
|
|||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
use uapi::{c, Errno, OwnedFd};
|
||||
use crate::utils::rc_eq::rc_eq;
|
||||
|
||||
const POINTER: u32 = 1;
|
||||
const KEYBOARD: u32 = 2;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use thiserror::Error;
|
|||
pub struct ZxdgDecorationManagerV1Global {
|
||||
name: GlobalName,
|
||||
}
|
||||
|
||||
impl ZxdgDecorationManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
|
|
|
|||
157
src/logger.rs
Normal file
157
src/logger.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
use crate::utils::errorfmt::ErrorFmt;
|
||||
use crate::utils::oserror::OsError;
|
||||
use crate::utils::ptr_ext::MutPtrExt;
|
||||
use bstr::{BStr, BString, ByteSlice};
|
||||
use log::{Level, Log, Metadata, Record};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fs::DirBuilder;
|
||||
use std::io::Write;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
use std::os::unix::fs::DirBuilderExt;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
use uapi::{c, format_ustr, Errno, Fd, OwnedFd};
|
||||
|
||||
#[thread_local]
|
||||
static BUFFER: UnsafeCell<Vec<u8>> = UnsafeCell::new(Vec::new());
|
||||
|
||||
pub struct Logger {
|
||||
level: AtomicU32,
|
||||
path: BString,
|
||||
file: OwnedFd,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn install_stderr(level: Level) -> Arc<Self> {
|
||||
let file = match uapi::fcntl_dupfd_cloexec(2, 0) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
let e = OsError::from(e);
|
||||
eprintln!("Error: Could not dup stderr: {}", ErrorFmt(e));
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
Self::install(level, b"", file)
|
||||
}
|
||||
|
||||
pub fn install_compositor(level: Level) -> Arc<Self> {
|
||||
let log_dir = create_log_dir();
|
||||
let (path, file) = 'file: {
|
||||
for i in 0.. {
|
||||
let file_name = format_ustr!(
|
||||
"{}/jay-{}-{}.txt",
|
||||
log_dir,
|
||||
humantime::format_rfc3339_millis(SystemTime::now()),
|
||||
i,
|
||||
);
|
||||
match uapi::open(
|
||||
&file_name,
|
||||
c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY,
|
||||
0o644,
|
||||
) {
|
||||
Ok(f) => break 'file (file_name, f),
|
||||
Err(Errno(c::EEXIST)) => {}
|
||||
Err(e) => {
|
||||
let e: OsError = e.into();
|
||||
eprintln!("Error: Could not create log file: {}", ErrorFmt(e));
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
};
|
||||
Self::install(level, path.as_bytes(), file)
|
||||
}
|
||||
|
||||
fn install(level: Level, path: &[u8], file: OwnedFd) -> Arc<Self> {
|
||||
let slf = Arc::new(Self {
|
||||
level: AtomicU32::new(level as _),
|
||||
path: path.to_vec().into(),
|
||||
file,
|
||||
});
|
||||
log::set_boxed_logger(Box::new(LogWrapper {
|
||||
logger: slf.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
log::set_max_level(level.to_level_filter());
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn set_level(&self, level: Level) {
|
||||
self.level.store(level as _, Relaxed);
|
||||
log::set_max_level(level.to_level_filter());
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &BStr {
|
||||
self.path.as_bstr()
|
||||
}
|
||||
}
|
||||
|
||||
fn create_log_dir() -> BString {
|
||||
let mut log_dir = match dirs::data_local_dir() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("Error: $HOME is not set");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
log_dir.push("jay");
|
||||
let res = DirBuilder::new()
|
||||
.recursive(true)
|
||||
.mode(0o755)
|
||||
.create(&log_dir);
|
||||
if let Err(e) = res {
|
||||
eprintln!(
|
||||
"Error: Could not create log directory {}: {}",
|
||||
log_dir.display(),
|
||||
ErrorFmt(e)
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
log_dir.into_os_string().into_vec().into()
|
||||
}
|
||||
|
||||
struct LogWrapper {
|
||||
logger: Arc<Logger>,
|
||||
}
|
||||
|
||||
impl Log for LogWrapper {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.level() as u32 <= self.logger.level.load(Relaxed)
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if record.level() as u32 > self.logger.level.load(Relaxed) {
|
||||
return;
|
||||
}
|
||||
let buffer = unsafe { BUFFER.get().deref_mut() };
|
||||
buffer.clear();
|
||||
let now = SystemTime::now();
|
||||
let _ = if let Some(mp) = record.module_path() {
|
||||
writeln!(
|
||||
buffer,
|
||||
"[{} {:5} {}] {}",
|
||||
humantime::format_rfc3339_millis(now),
|
||||
record.level(),
|
||||
mp,
|
||||
record.args(),
|
||||
)
|
||||
} else {
|
||||
writeln!(
|
||||
buffer,
|
||||
"[{} {:5}] {}",
|
||||
humantime::format_rfc3339_millis(now),
|
||||
record.level(),
|
||||
record.args(),
|
||||
)
|
||||
};
|
||||
let mut fd = Fd::new(self.logger.file.raw());
|
||||
let _ = fd.write_all(buffer);
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
|
@ -397,3 +397,10 @@ macro_rules! atoms {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fatal {
|
||||
($($arg:tt)+) => {{
|
||||
log::error!($($arg)+);
|
||||
std::process::exit(1);
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
15
src/main.rs
15
src/main.rs
|
|
@ -27,13 +27,9 @@
|
|||
clippy::option_map_unit_fn,
|
||||
clippy::wrong_self_convention,
|
||||
clippy::single_char_add_str,
|
||||
clippy::ptr_eq,
|
||||
clippy::ptr_eq
|
||||
)]
|
||||
|
||||
use crate::cli::{Cli, Cmd};
|
||||
use crate::compositor::start_compositor;
|
||||
use clap::Parser;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
#[macro_use]
|
||||
|
|
@ -58,6 +54,7 @@ mod format;
|
|||
mod globals;
|
||||
mod ifs;
|
||||
mod libinput;
|
||||
mod logger;
|
||||
mod logind;
|
||||
mod object;
|
||||
mod pango;
|
||||
|
|
@ -70,6 +67,7 @@ mod tasks;
|
|||
mod text;
|
||||
mod theme;
|
||||
mod time;
|
||||
mod tools;
|
||||
mod tree;
|
||||
mod udev;
|
||||
mod utils;
|
||||
|
|
@ -82,10 +80,5 @@ mod xkbcommon;
|
|||
mod xwayland;
|
||||
|
||||
fn main() {
|
||||
let cli: Cli = Cli::parse();
|
||||
println!("{:?}", cli);
|
||||
match cli.command {
|
||||
Cmd::Run => start_compositor(),
|
||||
_ => {}
|
||||
}
|
||||
cli::main();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,10 +212,7 @@ impl PangoCairoContext {
|
|||
if l.is_null() {
|
||||
return Err(PangoError::CreateLayout);
|
||||
}
|
||||
Ok(PangoLayout {
|
||||
c: self.clone(),
|
||||
l,
|
||||
})
|
||||
Ok(PangoLayout { c: self.clone(), l })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ use crate::render::Texture;
|
|||
use crate::state::State;
|
||||
use crate::theme::Color;
|
||||
use crate::tree::{ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode};
|
||||
use crate::utils::rc_eq::rc_eq;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use crate::utils::rc_eq::rc_eq;
|
||||
|
||||
pub struct Renderer<'a> {
|
||||
pub(super) ctx: &'a Rc<RenderContext>,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ struct Sighand {
|
|||
}
|
||||
|
||||
impl EventLoopDispatcher for Sighand {
|
||||
fn dispatch(self: Rc<Self>, events: i32) -> Result<(), Box<dyn Error>> {
|
||||
fn dispatch(self: Rc<Self>, _fd: Option<i32>, events: i32) -> Result<(), Box<dyn Error>> {
|
||||
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
|
||||
return Err(Box::new(SighandError::ErrorEvent));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::globals::{Globals, GlobalsError, WaylandGlobal};
|
|||
use crate::ifs::wl_output::WlOutputGlobal;
|
||||
use crate::ifs::wl_seat::{SeatIds, WlSeatGlobal};
|
||||
use crate::ifs::wl_surface::NoneSurfaceExt;
|
||||
use crate::logger::Logger;
|
||||
use crate::rect::Rect;
|
||||
use crate::render::RenderContext;
|
||||
use crate::theme::Theme;
|
||||
|
|
@ -66,6 +67,7 @@ pub struct State {
|
|||
pub pending_float_titles: AsyncQueue<Rc<FloatNode>>,
|
||||
pub dbus: Dbus,
|
||||
pub fdcloser: Arc<FdCloser>,
|
||||
pub logger: Arc<Logger>,
|
||||
}
|
||||
|
||||
pub struct InputDeviceData {
|
||||
|
|
|
|||
1
src/tools.rs
Normal file
1
src/tools.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod tool_client;
|
||||
386
src/tools/tool_client.rs
Normal file
386
src/tools/tool_client.rs
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
use crate::async_engine::{AsyncEngine, AsyncError, SpawnedFuture};
|
||||
use crate::client::{EventFormatter, RequestParser};
|
||||
use crate::event_loop::{EventLoop, EventLoopError};
|
||||
use crate::logger::Logger;
|
||||
use crate::object::{ObjectId, WL_DISPLAY_ID};
|
||||
use crate::utils::asyncevent::AsyncEvent;
|
||||
use crate::utils::bitfield::Bitfield;
|
||||
use crate::utils::buffd::{
|
||||
BufFdError, BufFdIn, BufFdOut, MsgFormatter, MsgParser, MsgParserError, OutBuffer,
|
||||
OutBufferSwapchain,
|
||||
};
|
||||
use crate::utils::errorfmt::ErrorFmt;
|
||||
use crate::utils::numcell::NumCell;
|
||||
use crate::utils::oserror::OsError;
|
||||
use crate::utils::stack::Stack;
|
||||
use crate::utils::vec_ext::VecExt;
|
||||
use crate::wheel::{Wheel, WheelError};
|
||||
use crate::wire::{wl_callback, wl_display, WlCallbackId};
|
||||
use ahash::AHashMap;
|
||||
use log::Level;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::future::{Future, Pending};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use uapi::{c, format_ustr};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ToolClientError {
|
||||
#[error("Could not create an event loop")]
|
||||
CreateEventLoop(#[source] EventLoopError),
|
||||
#[error("Could not create a timer wheel")]
|
||||
CreateWheel(#[source] WheelError),
|
||||
#[error("Could not create an async engine")]
|
||||
CreateEngine(#[source] AsyncError),
|
||||
#[error("XDG_RUNTIME_DIR is not set")]
|
||||
XrdNotSet,
|
||||
#[error("WAYLAND_DISPLAY is not set")]
|
||||
WaylandDisplayNotSet,
|
||||
#[error("Could not create a socket")]
|
||||
CreateSocket(#[source] OsError),
|
||||
#[error("The socket path is too long")]
|
||||
SocketPathTooLong,
|
||||
#[error("Could not connect to the compositor")]
|
||||
Connect(#[source] OsError),
|
||||
#[error("Could not create an async fd")]
|
||||
AsyncFd(#[source] AsyncError),
|
||||
#[error("The message length is smaller than 8 bytes")]
|
||||
MsgLenTooSmall,
|
||||
#[error("The size of the message is not a multiple of 4")]
|
||||
UnalignedMessage,
|
||||
#[error(transparent)]
|
||||
BufFdError(#[from] BufFdError),
|
||||
#[error("The size of the message is not a multiple of 4")]
|
||||
Parsing(&'static str, MsgParserError),
|
||||
#[error("Could not read from the compositor")]
|
||||
Read(#[source] BufFdError),
|
||||
#[error("Could not write to the compositor")]
|
||||
Write(#[source] BufFdError),
|
||||
}
|
||||
|
||||
pub struct ToolClient {
|
||||
pub logger: Arc<Logger>,
|
||||
pub el: Rc<EventLoop>,
|
||||
pub wheel: Rc<Wheel>,
|
||||
pub eng: Rc<AsyncEngine>,
|
||||
obj_ids: RefCell<Bitfield>,
|
||||
handlers: RefCell<
|
||||
AHashMap<
|
||||
ObjectId,
|
||||
AHashMap<u32, Rc<dyn Fn(&mut MsgParser) -> Result<(), ToolClientError>>>,
|
||||
>,
|
||||
>,
|
||||
bufs: Stack<Vec<u32>>,
|
||||
swapchain: Rc<RefCell<OutBufferSwapchain>>,
|
||||
flush_request: AsyncEvent,
|
||||
pending_futures: RefCell<AHashMap<u32, SpawnedFuture<()>>>,
|
||||
next_id: NumCell<u32>,
|
||||
incoming: Cell<Option<SpawnedFuture<()>>>,
|
||||
outgoing: Cell<Option<SpawnedFuture<()>>>,
|
||||
}
|
||||
|
||||
impl ToolClient {
|
||||
pub fn new(level: Level) -> Rc<Self> {
|
||||
match Self::try_new(level) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
fatal!("Could not create a tool client: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run<F>(&self, f: F)
|
||||
where
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
let _future = self.eng.spawn(async move {
|
||||
f.await;
|
||||
std::process::exit(0);
|
||||
});
|
||||
if let Err(e) = self.el.run() {
|
||||
fatal!("A fatal error occurred: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_new(level: Level) -> Result<Rc<Self>, ToolClientError> {
|
||||
let logger = Logger::install_stderr(level);
|
||||
let el = match EventLoop::new() {
|
||||
Ok(e) => e,
|
||||
Err(e) => return Err(ToolClientError::CreateEventLoop(e)),
|
||||
};
|
||||
let wheel = match Wheel::install(&el) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return Err(ToolClientError::CreateWheel(e)),
|
||||
};
|
||||
let eng = match AsyncEngine::install(&el, &wheel) {
|
||||
Ok(e) => e,
|
||||
Err(e) => return Err(ToolClientError::CreateEngine(e)),
|
||||
};
|
||||
let xrd = match std::env::var("XDG_RUNTIME_DIR") {
|
||||
Ok(d) => d,
|
||||
Err(_) => return Err(ToolClientError::XrdNotSet),
|
||||
};
|
||||
let wd = match std::env::var("WAYLAND_DISPLAY") {
|
||||
Ok(d) => d,
|
||||
Err(_) => return Err(ToolClientError::WaylandDisplayNotSet),
|
||||
};
|
||||
let path = format_ustr!("{}/{}.jay", xrd, wd);
|
||||
let socket = match uapi::socket(
|
||||
c::AF_UNIX,
|
||||
c::SOCK_STREAM | c::SOCK_CLOEXEC | c::SOCK_NONBLOCK,
|
||||
0,
|
||||
) {
|
||||
Ok(s) => Rc::new(s),
|
||||
Err(e) => return Err(ToolClientError::CreateSocket(e.into())),
|
||||
};
|
||||
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
||||
addr.sun_family = c::AF_UNIX as _;
|
||||
if path.len() >= addr.sun_path.len() {
|
||||
return Err(ToolClientError::SocketPathTooLong);
|
||||
}
|
||||
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;
|
||||
if let Err(e) = uapi::connect(socket.raw(), &addr) {
|
||||
return Err(ToolClientError::Connect(e.into()));
|
||||
}
|
||||
let fd = match eng.fd(&socket) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => return Err(ToolClientError::AsyncFd(e)),
|
||||
};
|
||||
let mut obj_ids = Bitfield::default();
|
||||
obj_ids.take(0);
|
||||
obj_ids.take(1);
|
||||
let slf = Rc::new(Self {
|
||||
logger,
|
||||
el,
|
||||
wheel,
|
||||
eng,
|
||||
obj_ids: RefCell::new(obj_ids),
|
||||
handlers: Default::default(),
|
||||
bufs: Default::default(),
|
||||
swapchain: Default::default(),
|
||||
flush_request: Default::default(),
|
||||
pending_futures: Default::default(),
|
||||
next_id: Default::default(),
|
||||
incoming: Default::default(),
|
||||
outgoing: Default::default(),
|
||||
});
|
||||
wl_display::Error::handle(&slf, WL_DISPLAY_ID, (), |_, val| {
|
||||
fatal!("The compositor returned a fatal error: {}", val.message);
|
||||
});
|
||||
wl_display::DeleteId::handle(&slf, WL_DISPLAY_ID, slf.clone(), |tc, val| {
|
||||
tc.obj_ids.borrow_mut().release(val.id);
|
||||
});
|
||||
slf.incoming.set(Some(
|
||||
slf.eng.spawn(
|
||||
Incoming {
|
||||
tc: slf.clone(),
|
||||
buf: BufFdIn::new(fd.clone()),
|
||||
}
|
||||
.run(),
|
||||
),
|
||||
));
|
||||
slf.outgoing.set(Some(
|
||||
slf.eng.spawn(
|
||||
Outgoing {
|
||||
tc: slf.clone(),
|
||||
buf: BufFdOut::new(fd.clone()),
|
||||
buffers: Default::default(),
|
||||
}
|
||||
.run(),
|
||||
),
|
||||
));
|
||||
Ok(slf)
|
||||
}
|
||||
|
||||
fn handle<T, F, R, H>(self: &Rc<Self>, id: ObjectId, recv: R, h: H)
|
||||
where
|
||||
T: RequestParser<'static>,
|
||||
F: Future<Output = ()> + 'static,
|
||||
R: 'static,
|
||||
H: for<'a> Fn(&R, T::Generic<'a>) -> Option<F> + 'static,
|
||||
{
|
||||
let slf = self.clone();
|
||||
let mut handlers = self.handlers.borrow_mut();
|
||||
handlers.entry(id.into()).or_default().insert(
|
||||
T::ID,
|
||||
Rc::new(move |parser| {
|
||||
let val = match <T::Generic<'_> as RequestParser<'_>>::parse(parser) {
|
||||
Ok(val) => val,
|
||||
Err(e) => return Err(ToolClientError::Parsing(std::any::type_name::<T>(), e)),
|
||||
};
|
||||
let res = h(&recv, val);
|
||||
if let Some(res) = res {
|
||||
let id = slf.next_id.fetch_add(1);
|
||||
let slf2 = slf.clone();
|
||||
let future = slf.eng.spawn(async move {
|
||||
res.await;
|
||||
slf2.pending_futures.borrow_mut().remove(&id);
|
||||
});
|
||||
slf.pending_futures.borrow_mut().insert(id, future);
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn send<M: EventFormatter>(&self, msg: M) {
|
||||
let mut fds = vec![];
|
||||
let mut swapchain = self.swapchain.borrow_mut();
|
||||
let mut fmt = MsgFormatter::new(&mut swapchain.cur, &mut fds);
|
||||
msg.format(&mut fmt);
|
||||
fmt.write_len();
|
||||
if swapchain.cur.is_full() {
|
||||
swapchain.commit();
|
||||
}
|
||||
self.flush_request.trigger();
|
||||
}
|
||||
|
||||
pub fn id<T: From<ObjectId>>(&self) -> T {
|
||||
let id = self.obj_ids.borrow_mut().acquire();
|
||||
ObjectId::from_raw(id).into()
|
||||
}
|
||||
|
||||
pub async fn round_trip(self: &Rc<Self>) {
|
||||
let callback: WlCallbackId = self.id();
|
||||
self.send(wl_display::Sync {
|
||||
self_id: WL_DISPLAY_ID,
|
||||
callback,
|
||||
});
|
||||
let ah = Rc::new(AsyncEvent::default());
|
||||
wl_callback::Done::handle(self, callback, ah.clone(), |ah, _| {
|
||||
ah.trigger();
|
||||
});
|
||||
ah.triggered().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub const NONE_FUTURE: Option<Pending<()>> = None;
|
||||
|
||||
pub trait Handle: RequestParser<'static> {
|
||||
fn handle<R, H>(tl: &Rc<ToolClient>, id: impl Into<ObjectId>, r: R, h: H)
|
||||
where
|
||||
R: 'static,
|
||||
H: for<'a> Fn(&R, Self::Generic<'a>) + 'static;
|
||||
|
||||
fn handle2<R, F, H>(tl: &Rc<ToolClient>, id: impl Into<ObjectId>, r: R, h: H)
|
||||
where
|
||||
R: 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
H: for<'a> Fn(&R, Self::Generic<'a>) -> F + 'static;
|
||||
}
|
||||
|
||||
impl<T: RequestParser<'static>> Handle for T {
|
||||
fn handle<R, H>(tl: &Rc<ToolClient>, id: impl Into<ObjectId>, r: R, h: H)
|
||||
where
|
||||
R: 'static,
|
||||
H: for<'a> Fn(&R, T::Generic<'a>) + 'static,
|
||||
{
|
||||
tl.handle::<Self, _, _, _>(id.into(), r, move |a, b| {
|
||||
h(a, b);
|
||||
NONE_FUTURE
|
||||
});
|
||||
}
|
||||
|
||||
fn handle2<R, F, H>(tl: &Rc<ToolClient>, id: impl Into<ObjectId>, r: R, h: H)
|
||||
where
|
||||
R: 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
H: for<'a> Fn(&R, T::Generic<'a>) -> F + 'static,
|
||||
{
|
||||
tl.handle::<Self, _, _, _>(id.into(), r, move |a, b| Some(h(a, b)));
|
||||
}
|
||||
}
|
||||
|
||||
struct Outgoing {
|
||||
tc: Rc<ToolClient>,
|
||||
buf: BufFdOut,
|
||||
buffers: VecDeque<OutBuffer>,
|
||||
}
|
||||
|
||||
impl Outgoing {
|
||||
async fn run(mut self: Self) {
|
||||
loop {
|
||||
self.tc.flush_request.triggered().await;
|
||||
if let Err(e) = self.flush().await {
|
||||
fatal!("Could not process an outgoing message: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), ToolClientError> {
|
||||
{
|
||||
let mut swapchain = self.tc.swapchain.borrow_mut();
|
||||
swapchain.commit();
|
||||
mem::swap(&mut swapchain.pending, &mut self.buffers);
|
||||
}
|
||||
while let Some(mut cur) = self.buffers.pop_front() {
|
||||
if let Err(e) = self.buf.flush_no_timeout(&mut cur).await {
|
||||
return Err(ToolClientError::Write(e));
|
||||
}
|
||||
self.tc.swapchain.borrow_mut().free.push(cur);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Incoming {
|
||||
tc: Rc<ToolClient>,
|
||||
buf: BufFdIn,
|
||||
}
|
||||
|
||||
impl Incoming {
|
||||
async fn run(mut self: Self) {
|
||||
loop {
|
||||
if let Err(e) = self.handle_msg().await {
|
||||
fatal!("Could not process an incoming message: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_msg(&mut self) -> Result<(), ToolClientError> {
|
||||
let mut hdr = [0u32, 0];
|
||||
if let Err(e) = self.buf.read_full(&mut hdr[..]).await {
|
||||
return Err(ToolClientError::Read(e));
|
||||
}
|
||||
let obj_id = ObjectId::from_raw(hdr[0]);
|
||||
let len = (hdr[1] >> 16) as usize;
|
||||
let request = hdr[1] & 0xffff;
|
||||
if len < 8 {
|
||||
return Err(ToolClientError::MsgLenTooSmall);
|
||||
}
|
||||
if len % 4 != 0 {
|
||||
return Err(ToolClientError::UnalignedMessage);
|
||||
}
|
||||
let len = len / 4 - 2;
|
||||
let mut data_buf = self.tc.bufs.pop().unwrap_or_default();
|
||||
data_buf.clear();
|
||||
data_buf.reserve(len);
|
||||
let unused = data_buf.split_at_spare_mut_ext().1;
|
||||
if let Err(e) = self.buf.read_full(&mut unused[..len]).await {
|
||||
return Err(ToolClientError::Read(e));
|
||||
}
|
||||
unsafe {
|
||||
data_buf.set_len(len);
|
||||
}
|
||||
let mut handler = None;
|
||||
{
|
||||
let handlers = self.tc.handlers.borrow_mut();
|
||||
if let Some(handlers) = handlers.get(&obj_id) {
|
||||
handler = handlers.get(&request).cloned();
|
||||
}
|
||||
}
|
||||
if let Some(handler) = handler {
|
||||
let mut parser = MsgParser::new(&mut self.buf, &data_buf);
|
||||
handler(&mut parser)?;
|
||||
}
|
||||
if data_buf.capacity() > 0 {
|
||||
self.tc.bufs.push(data_buf);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@ use crate::state::State;
|
|||
use crate::text;
|
||||
use crate::utils::errorfmt::ErrorFmt;
|
||||
use crate::utils::numcell::NumCell;
|
||||
use crate::utils::rc_eq::rc_eq;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut, Sub};
|
||||
use std::rc::Rc;
|
||||
use crate::utils::rc_eq::rc_eq;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub mod numcell;
|
|||
pub mod oserror;
|
||||
pub mod ptr_ext;
|
||||
pub mod queue;
|
||||
pub mod rc_eq;
|
||||
pub mod run_toplevel;
|
||||
pub mod smallmap;
|
||||
pub mod stack;
|
||||
|
|
@ -25,4 +26,3 @@ pub mod trim;
|
|||
pub mod vasprintf;
|
||||
pub mod vec_ext;
|
||||
pub mod vecstorage;
|
||||
pub mod rc_eq;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,15 @@ pub struct Bitfield {
|
|||
}
|
||||
|
||||
impl Bitfield {
|
||||
pub fn take(&mut self, val: u32) {
|
||||
let idx = val as usize / SEG_SIZE;
|
||||
let pos = val as usize % SEG_SIZE;
|
||||
while self.vals.len() <= idx {
|
||||
self.vals.push(0);
|
||||
}
|
||||
self.vals[idx] &= !(1 << pos);
|
||||
}
|
||||
|
||||
pub fn acquire(&mut self) -> u32 {
|
||||
for (idx, n) in self.vals.iter_mut().enumerate() {
|
||||
if *n != 0 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::async_engine::AsyncError;
|
||||
pub use buf_in::BufFdIn;
|
||||
pub use buf_out::{BufFdOut, OutBufferSwapchain};
|
||||
pub use buf_out::{BufFdOut, OutBuffer, OutBufferSwapchain};
|
||||
pub use formatter::MsgFormatter;
|
||||
pub use parser::{MsgParser, MsgParserError};
|
||||
use thiserror::Error;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,17 @@ impl BufFdOut {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn flush_no_timeout(&mut self, buf: &mut OutBuffer) -> Result<(), BufFdError> {
|
||||
while buf.read_pos < buf.write_pos {
|
||||
if self.flush_sync(buf)? {
|
||||
self.fd.writable().await?;
|
||||
}
|
||||
}
|
||||
buf.read_pos = 0;
|
||||
buf.write_pos = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_sync(&mut self, buffer: &mut OutBuffer) -> Result<bool, BufFdError> {
|
||||
while buffer.read_pos < buffer.write_pos {
|
||||
let mut buf = unsafe { &(*buffer.buf)[buffer.read_pos..buffer.write_pos] };
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ impl<'a, 'b> MsgParser<'a, 'b> {
|
|||
Self {
|
||||
buf,
|
||||
pos: 0,
|
||||
data: unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) },
|
||||
data: uapi::as_bytes(data),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,11 @@ struct WheelWrapper {
|
|||
}
|
||||
|
||||
impl EventLoopDispatcher for WheelWrapper {
|
||||
fn dispatch(self: Rc<Self>, events: i32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn dispatch(
|
||||
self: Rc<Self>,
|
||||
_fd: Option<i32>,
|
||||
events: i32,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
|
||||
return Err(Box::new(WheelError::ErrorEvent));
|
||||
}
|
||||
|
|
@ -238,7 +242,7 @@ struct PeriodicDispatcher {
|
|||
}
|
||||
|
||||
impl EventLoopDispatcher for PeriodicDispatcher {
|
||||
fn dispatch(self: Rc<Self>, events: i32) -> Result<(), Box<dyn Error>> {
|
||||
fn dispatch(self: Rc<Self>, _fd: Option<i32>, events: i32) -> Result<(), Box<dyn Error>> {
|
||||
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
|
||||
return Err(Box::new(WheelError::ErrorEvent));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
#![allow(unused_imports, unused_variables, dead_code, unused_assignments, clippy::eval_order_dependence, clippy::double_parens, clippy::unnecessary_cast)]
|
||||
#![allow(
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
dead_code,
|
||||
unused_assignments,
|
||||
clippy::eval_order_dependence,
|
||||
clippy::double_parens,
|
||||
clippy::unnecessary_cast
|
||||
)]
|
||||
|
||||
use crate::xcon::{Formatter, Message, Parser, Request, XEvent, XconError};
|
||||
use bstr::BStr;
|
||||
|
|
|
|||
|
|
@ -161,9 +161,15 @@ async fn run(
|
|||
};
|
||||
let client_id = state.clients.id();
|
||||
let queue = Rc::new(AsyncQueue::new());
|
||||
let client = state
|
||||
.clients
|
||||
.spawn2(client_id, state, client1, 9999, 9999, Some(queue.clone()));
|
||||
let client = state.clients.spawn2(
|
||||
client_id,
|
||||
state,
|
||||
client1,
|
||||
9999,
|
||||
9999,
|
||||
true,
|
||||
Some(queue.clone()),
|
||||
);
|
||||
let client = match client {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(XWaylandError::SpawnClient(e)),
|
||||
|
|
|
|||
9
wire/jay_compositor.txt
Normal file
9
wire/jay_compositor.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg get_log_file = 1 {
|
||||
id: id(jay_log_file),
|
||||
}
|
||||
10
wire/jay_log_file.txt
Normal file
10
wire/jay_log_file.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
}
|
||||
|
||||
# events
|
||||
|
||||
msg path = 0 {
|
||||
path: bstr,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue