1
0
Fork 0
forked from wry/wry

autocommit 2022-03-30 03:00:46 CEST

This commit is contained in:
Julian Orth 2022-03-30 03:00:46 +02:00
parent 9842264fad
commit 28c9b46400
40 changed files with 1212 additions and 175 deletions

102
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,7 +11,7 @@ use thiserror::Error;
pub struct WlRegistry {
id: WlRegistryId,
client: Rc<Client>,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
}

View file

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

View file

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

View file

@ -397,3 +397,10 @@ macro_rules! atoms {
}
}
}
macro_rules! fatal {
($($arg:tt)+) => {{
log::error!($($arg)+);
std::process::exit(1);
}}
}

View file

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

View file

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

View file

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

View file

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

View file

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

@ -0,0 +1 @@
pub mod tool_client;

386
src/tools/tool_client.rs Normal file
View 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(())
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

@ -0,0 +1,10 @@
# requests
msg destroy = 0 {
}
# events
msg path = 0 {
path: bstr,
}