From 5f13954dbcf3476cfe20aebea507c6f2439b423f Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 14 Apr 2022 19:52:11 +0200 Subject: [PATCH] autocommit 2022-04-14 19:52:11 CEST --- default-config/src/lib.rs | 3 +- jay-config/src/_private/client.rs | 4 + jay-config/src/_private/ipc.rs | 4 + jay-config/src/lib.rs | 4 + src/acceptor.rs | 11 +- src/backend.rs | 12 + src/backends/dummy.rs | 10 +- src/backends/metal.rs | 115 ++++--- src/backends/metal/video.rs | 2 +- src/backends/x.rs | 281 +++++++++--------- src/cli.rs | 2 +- src/compositor.rs | 231 +++++++++----- src/config/handler.rs | 12 +- src/dbus.rs | 26 +- src/forker.rs | 5 +- src/main.rs | 1 + src/state.rs | 22 +- src/tasks.rs | 3 +- src/tasks/idle.rs | 3 + src/tasks/start_backend.rs | 29 -- src/tools/tool_client.rs | 10 +- src/user_session.rs | 54 ++++ src/utils.rs | 1 + src/utils/xrd.rs | 5 + src/xcon.rs | 3 +- src/xwayland.rs | 11 +- .../org.freedesktop.systemd1.Manager.txt | 4 + 27 files changed, 556 insertions(+), 312 deletions(-) delete mode 100644 src/tasks/start_backend.rs create mode 100644 src/user_session.rs create mode 100644 src/utils/xrd.rs create mode 100644 wire-dbus/org.freedesktop.systemd1.Manager.txt diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index a275a338..4a0320b7 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -18,7 +18,7 @@ use { SYM_F23, SYM_F24, SYM_F25, SYM_F3, SYM_F4, SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9, }, }, - quit, + quit, set_env, status::set_status, switch_to_vt, Axis::{Horizontal, Vertical}, @@ -143,6 +143,7 @@ pub fn configure() { timer.on_tick(update_status); } + set_env("GTK_THEME", "Adwaita:dark"); Command::new("mako").spawn(); } diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 2959a9d6..61f51677 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -318,6 +318,10 @@ impl Client { self.send(&ClientMessage::SetMono { seat, mono }); } + pub fn set_env(&self, key: &str, val: &str) { + self.send(&ClientMessage::SetEnv { key, val }); + } + pub fn set_status(&self, status: &str) { self.send(&ClientMessage::SetStatus { status }); } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index fcf9fb26..d50a3df9 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -221,6 +221,10 @@ pub enum ClientMessage<'a> { initial: Option, periodic: Option, }, + SetEnv { + key: &'a str, + val: &'a str, + }, } #[derive(Encode, Decode, Debug)] diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index 7df35cfd..6f0d1f85 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -56,6 +56,10 @@ pub fn switch_to_vt(n: u32) { get!().switch_to_vt(n) } +pub fn set_env(key: &str, val: &str) { + get!().set_env(key, val); +} + pub struct Command { prog: String, args: Vec, diff --git a/src/acceptor.rs b/src/acceptor.rs index fbf3d2f6..1c4c4c1b 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -9,6 +9,7 @@ use { thiserror::Error, uapi::{c, format_ustr, Errno, OwnedFd, Ustring}, }; +use crate::utils::xrd::xrd; #[derive(Debug, Error)] pub enum AcceptorError { @@ -115,9 +116,9 @@ fn bind_socket( } fn allocate_socket() -> Result { - let xrd = match std::env::var("XDG_RUNTIME_DIR") { - Ok(d) => d, - Err(_) => return Err(AcceptorError::XrdNotSet), + let xrd = match xrd() { + Some(d) => d, + _ => return Err(AcceptorError::XrdNotSet), }; let mut fds = [None, None]; for fd in &mut fds { @@ -145,7 +146,7 @@ fn allocate_socket() -> Result { } impl Acceptor { - pub fn install(state: &Rc) -> Result { + pub fn install(state: &Rc) -> Result, AcceptorError> { let socket = allocate_socket()?; log::info!("bound to socket {}", socket.path.display()); for fd in [&socket.secure, &socket.insecure] { @@ -170,6 +171,8 @@ impl Acceptor { state .el .insert(id2, Some(acc.socket.secure.raw()), c::EPOLLIN, acc)?; + let name = Rc::new(name.display().to_string()); + state.socket_path.set(name.clone()); Ok(name) } } diff --git a/src/backend.rs b/src/backend.rs index 2dcfa462..217c1764 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,10 +1,12 @@ use { crate::{ + async_engine::SpawnedFuture, fixed::Fixed, ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, video::drm::ConnectorType, }, std::{ + error::Error, fmt::{Debug, Display, Formatter}, rc::Rc, }, @@ -14,6 +16,8 @@ linear_ids!(ConnectorIds, ConnectorId); linear_ids!(InputDeviceIds, InputDeviceId); pub trait Backend { + fn run(self: Rc) -> SpawnedFuture>>; + fn switch_to(&self, vtnr: u32) { let _ = vtnr; } @@ -21,6 +25,14 @@ pub trait Backend { fn set_idle(&self, idle: bool) { let _ = idle; } + + fn supports_idle(&self) -> bool { + false + } + + fn is_freestanding(&self) -> bool { + false + } } #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] diff --git a/src/backends/dummy.rs b/src/backends/dummy.rs index aa3a19b0..5aca1634 100644 --- a/src/backends/dummy.rs +++ b/src/backends/dummy.rs @@ -1,14 +1,20 @@ +use std::error::Error; use { crate::{ + async_engine::SpawnedFuture, backend::{Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId}, video::drm::ConnectorType, }, std::rc::Rc, }; -pub struct DummyBackend {} +pub struct DummyBackend; -impl Backend for DummyBackend {} +impl Backend for DummyBackend { + fn run(self: Rc) -> SpawnedFuture>> { + unreachable!(); + } +} pub struct DummyOutput { pub id: ConnectorId, diff --git a/src/backends/metal.rs b/src/backends/metal.rs index ffeba71b..3accc475 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -4,13 +4,13 @@ mod video; use { crate::{ - async_engine::{AsyncError, AsyncFd, Phase}, + async_engine::{AsyncError, AsyncFd, SpawnedFuture}, backend::{ Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent, KeyState, }, backends::metal::video::{MetalDrmDevice, PendingDrmDevice}, - dbus::DbusError, + dbus::{DbusError, SignalHandler}, libinput::{ consts::{ AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, @@ -25,7 +25,6 @@ use { logind::{LogindError, Session}, render::RenderError, state::State, - tasks::idle, udev::{Udev, UdevError, UdevMonitor}, utils::{ clonecell::{CloneCell, UnsafeCellCloneSafe}, @@ -42,6 +41,7 @@ use { }, std::{ cell::{Cell, RefCell}, + error::Error, ffi::{CStr, CString}, future::pending, mem, @@ -67,8 +67,6 @@ pub enum MetalError { LibInput(#[from] LibInputError), #[error("Dupfd failed")] Dup(#[source] crate::utils::oserror::OsError), - #[error("Metal backend terminated unexpectedly")] - UnexpectedTermination, #[error("Could not create GBM device")] GbmDevice(#[source] GbmError), #[error("Could not update the drm properties")] @@ -99,20 +97,17 @@ pub enum MetalError { CreateEncoder(#[source] DrmError), #[error(transparent)] DrmError(#[from] DrmError), - #[error("Could not create an async fd for the drm fd")] - CreateDrmAsyncFd(#[source] AsyncError), -} - -pub async fn run(state: Rc) -> MetalError { - match run_(state).await { - Err(e) => e, - _ => MetalError::UnexpectedTermination, - } + #[error("Could not create an async fd")] + CreateAsyncFd(#[source] AsyncError), + #[error("Could not create device-paused signal handler")] + DevicePauseSignalHandler(#[source] DbusError), + #[error("Could not create device-resumed signal handler")] + DeviceResumeSignalHandler(#[source] DbusError), } linear_ids!(DrmIds, DrmId); -struct MetalBackend { +pub struct MetalBackend { state: Rc, udev: Rc, monitor: Rc, @@ -122,9 +117,30 @@ struct MetalBackend { device_holder: Rc, session: Session, drm_ids: DrmIds, + pause_handler: Cell>, + resume_handler: Cell>, +} + +impl MetalBackend { + async fn run(self: Rc) -> Result<(), MetalError> { + let _monitor = self.state.eng.spawn(self.clone().monitor_devices()); + let _events = self.state.eng.spawn(self.clone().handle_libinput_events()); + if let Err(e) = self.enumerate_devices() { + return Err(MetalError::Enumerate(Box::new(e))); + } + pending().await + } } impl Backend for MetalBackend { + fn run(self: Rc) -> SpawnedFuture>> { + let slf = self.clone(); + self.state.eng.spawn(async move { + slf.run().await?; + Ok(()) + }) + } + fn switch_to(&self, vtnr: u32) { self.session.switch_to(vtnr, move |res| { if let Err(e) = res { @@ -160,9 +176,27 @@ impl Backend for MetalBackend { } } } + + fn supports_idle(&self) -> bool { + true + } + + fn is_freestanding(&self) -> bool { + true + } } -async fn run_(state: Rc) -> Result<(), MetalError> { +fn dup_async_fd(state: &Rc, fd: c::c_int) -> Result { + match uapi::fcntl_dupfd_cloexec(fd, 0) { + Ok(m) => match state.eng.fd(&Rc::new(m)) { + Ok(fd) => Ok(fd), + Err(e) => Err(MetalError::CreateAsyncFd(e)), + }, + Err(e) => Err(MetalError::Dup(e.into())), + } +} + +pub async fn create(state: &Rc) -> Result, MetalError> { let socket = match state.dbus.system() { Ok(s) => s, Err(e) => return Err(MetalError::DbusSystemSocket(e)), @@ -186,14 +220,8 @@ async fn run_(state: Rc) -> Result<(), MetalError> { monitor.add_match_subsystem_devtype(Some("drm"), None)?; monitor.enable_receiving()?; let libinput = Rc::new(LibInput::new(device_holder.clone())?); - let monitor_fd = match uapi::fcntl_dupfd_cloexec(monitor.fd(), 0) { - Ok(m) => state.eng.fd(&Rc::new(m)).unwrap(), - Err(e) => return Err(MetalError::Dup(e.into())), - }; - let libinput_fd = match uapi::fcntl_dupfd_cloexec(libinput.fd(), 0) { - Ok(m) => state.eng.fd(&Rc::new(m)).unwrap(), - Err(e) => return Err(MetalError::Dup(e.into())), - }; + let monitor_fd = dup_async_fd(&state, monitor.fd())?; + let libinput_fd = dup_async_fd(&state, libinput.fd())?; let metal = Rc::new(MetalBackend { state: state.clone(), udev, @@ -204,31 +232,28 @@ async fn run_(state: Rc) -> Result<(), MetalError> { device_holder, session, drm_ids: Default::default(), + pause_handler: Default::default(), + resume_handler: Default::default(), }); - let _pause_handler = { + metal.pause_handler.set(Some({ let mtl = metal.clone(); - metal - .session - .on_pause(move |p| mtl.handle_device_pause(p)) - .unwrap() - }; - let _resume_handler = { + let sh = metal.session.on_pause(move |p| mtl.handle_device_pause(p)); + match sh { + Ok(sh) => sh, + Err(e) => return Err(MetalError::DevicePauseSignalHandler(e)), + } + })); + metal.resume_handler.set(Some({ let mtl = metal.clone(); - metal + let sh = metal .session - .on_resume(move |p| mtl.handle_device_resume(p)) - .unwrap() - }; - let _monitor = state.eng.spawn(metal.clone().monitor_devices()); - let _events = state.eng.spawn(metal.clone().handle_libinput_events()); - if let Err(e) = metal.enumerate_devices() { - return Err(MetalError::Enumerate(Box::new(e))); - } - state.backend.set(Some(metal.clone())); - let _idle = state - .eng - .spawn2(Phase::PostLayout, idle(state.clone(), metal.clone())); - pending().await + .on_resume(move |p| mtl.handle_device_resume(p)); + match sh { + Ok(sh) => sh, + Err(e) => return Err(MetalError::DeviceResumeSignalHandler(e)), + } + })); + Ok(metal) } struct MetalInputDevice { diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index db9361d2..ddedfd7f 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -540,7 +540,7 @@ impl MetalBackend { }; let async_fd = match self.state.eng.fd(master.fd()) { Ok(f) => f, - Err(e) => return Err(MetalError::CreateDrmAsyncFd(e)), + Err(e) => return Err(MetalError::CreateAsyncFd(e)), }; let dev = Rc::new(MetalDrmDeviceStatic { diff --git a/src/backends/x.rs b/src/backends/x.rs index e8a34196..c6aaeb5f 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -1,3 +1,4 @@ +use std::error::Error; use { crate::{ async_engine::{Phase, SpawnedFuture}, @@ -51,6 +52,7 @@ use { borrow::Cow, cell::{Cell, RefCell}, collections::VecDeque, + future::pending, rc::Rc, }, thiserror::Error, @@ -106,16 +108,137 @@ pub enum XBackendError { QueryDevice(#[source] XconError), } -pub struct XBackend { - _data: Rc, - _events: SpawnedFuture<()>, - _present: SpawnedFuture<()>, - _grab: SpawnedFuture<()>, +pub async fn create(state: &Rc) -> Result, XBackendError> { + let c = match Xcon::connect(state.eng.clone()).await { + Ok(c) => c, + Err(e) => return Err(XBackendError::CannotConnect(e)), + }; + if let Err(e) = c + .call(&XiQueryVersion { + major_version: 2, + minor_version: 2, + }) + .await + { + return Err(XBackendError::EnableXinput(e)); + } + if let Err(e) = c + .call(&Dri3QueryVersion { + major_version: 1, + minor_version: 0, + }) + .await + { + return Err(XBackendError::EnableDri3(e)); + } + if let Err(e) = c + .call(&PresentQueryVersion { + major_version: 1, + minor_version: 0, + }) + .await + { + return Err(XBackendError::EnablePresent(e)); + } + if let Err(e) = c + .call(&XkbUseExtension { + wanted_major: 1, + wanted_minor: 0, + }) + .await + { + return Err(XBackendError::EnableXkb(e)); + } + let root = c.setup().screens[0].root; + let drm = { + let res = c + .call(&Dri3Open { + drawable: root, + provider: 0, + }) + .await; + match res { + Ok(r) => Drm::reopen(r.get().device_fd.raw(), false)?, + Err(e) => return Err(XBackendError::DriOpen(e)), + } + }; + let gbm = GbmDevice::new(&drm)?; + let ctx = match RenderContext::from_drm_device(&drm) { + Ok(r) => Rc::new(r), + Err(e) => return Err(XBackendError::CreateEgl(e)), + }; + let cursor = { + let cp = CreatePixmap { + depth: 1, + pid: c.generate_id()?, + drawable: root, + width: 1, + height: 1, + }; + if let Err(e) = c.call(&cp).await { + return Err(XBackendError::CreatePixmap(e)); + } + let cc = CreateCursor { + cid: c.generate_id()?, + source: cp.pid, + mask: cp.pid, + fore_red: 0, + fore_green: 0, + fore_blue: 0, + back_red: 0, + back_green: 0, + back_blue: 0, + x: 0, + y: 0, + }; + if let Err(e) = c.call(&cc).await { + return Err(XBackendError::CreateCursor(e)); + } + c.call(&FreePixmap { pixmap: cp.pid }); + cc.cid + }; + { + let se = XiSelectEvents { + window: c.setup().screens[0].root, + masks: Cow::Borrowed(&[XiEventMask { + deviceid: INPUT_DEVICE_ALL, + mask: &[XI_EVENT_MASK_HIERARCHY], + }]), + }; + if let Err(e) = c.call(&se).await { + return Err(XBackendError::SelectHierarchyEvents(e)); + } + } + + let data = Rc::new(XBackend { + state: state.clone(), + c, + outputs: Default::default(), + seats: Default::default(), + mouse_seats: Default::default(), + ctx: ctx.clone(), + gbm, + cursor, + root, + scheduled_present: Default::default(), + grab_requests: Default::default(), + }); + data.add_output().await?; + + Ok(data) } -impl Backend for XBackend {} +impl Backend for XBackend { + fn run(self: Rc) -> SpawnedFuture>> { + let slf = self.clone(); + self.state.eng.spawn(async move { + slf.run().await?; + Ok(()) + }) + } +} -struct XBackendData { +pub struct XBackend { state: Rc, c: Rc, outputs: CopyHashMap>, @@ -130,141 +253,21 @@ struct XBackendData { } impl XBackend { - pub async fn run(state: &Rc) -> Result, XBackendError> { - let c = match Xcon::connect(state.eng.clone()).await { - Ok(c) => c, - Err(e) => return Err(XBackendError::CannotConnect(e)), - }; - if let Err(e) = c - .call(&XiQueryVersion { - major_version: 2, - minor_version: 2, - }) - .await - { - return Err(XBackendError::EnableXinput(e)); - } - if let Err(e) = c - .call(&Dri3QueryVersion { - major_version: 1, - minor_version: 0, - }) - .await - { - return Err(XBackendError::EnableDri3(e)); - } - if let Err(e) = c - .call(&PresentQueryVersion { - major_version: 1, - minor_version: 0, - }) - .await - { - return Err(XBackendError::EnablePresent(e)); - } - if let Err(e) = c - .call(&XkbUseExtension { - wanted_major: 1, - wanted_minor: 0, - }) - .await - { - return Err(XBackendError::EnableXkb(e)); - } - let root = c.setup().screens[0].root; - let drm = { - let res = c - .call(&Dri3Open { - drawable: root, - provider: 0, - }) - .await; - match res { - Ok(r) => Drm::reopen(r.get().device_fd.raw(), false)?, - Err(e) => return Err(XBackendError::DriOpen(e)), - } - }; - let gbm = GbmDevice::new(&drm)?; - let ctx = match RenderContext::from_drm_device(&drm) { - Ok(r) => Rc::new(r), - Err(e) => return Err(XBackendError::CreateEgl(e)), - }; - let cursor = { - let cp = CreatePixmap { - depth: 1, - pid: c.generate_id()?, - drawable: root, - width: 1, - height: 1, - }; - if let Err(e) = c.call(&cp).await { - return Err(XBackendError::CreatePixmap(e)); - } - let cc = CreateCursor { - cid: c.generate_id()?, - source: cp.pid, - mask: cp.pid, - fore_red: 0, - fore_green: 0, - fore_blue: 0, - back_red: 0, - back_green: 0, - back_blue: 0, - x: 0, - y: 0, - }; - if let Err(e) = c.call(&cc).await { - return Err(XBackendError::CreateCursor(e)); - } - c.call(&FreePixmap { pixmap: cp.pid }); - cc.cid - }; - { - let se = XiSelectEvents { - window: c.setup().screens[0].root, - masks: Cow::Borrowed(&[XiEventMask { - deviceid: INPUT_DEVICE_ALL, - mask: &[XI_EVENT_MASK_HIERARCHY], - }]), - }; - if let Err(e) = c.call(&se).await { - return Err(XBackendError::SelectHierarchyEvents(e)); - } - } + async fn run(self: Rc) -> Result<(), XBackendError> { + self.query_devices(INPUT_DEVICE_ALL_MASTER).await?; - let data = Rc::new(XBackendData { - state: state.clone(), - c, - outputs: Default::default(), - seats: Default::default(), - mouse_seats: Default::default(), - ctx: ctx.clone(), - gbm, - cursor, - root, - scheduled_present: Default::default(), - grab_requests: Default::default(), - }); - data.add_output().await?; - data.query_devices(INPUT_DEVICE_ALL_MASTER).await?; + let _events = self.state.eng.spawn(self.clone().event_handler()); + let _grab = self.state.eng.spawn(self.clone().grab_handler()); + let _present = self + .state + .eng + .spawn2(Phase::Present, self.clone().present_handler()); - let slf = Rc::new(Self { - _events: state.eng.spawn(data.clone().event_handler()), - _grab: state.eng.spawn(data.clone().grab_handler()), - _present: state - .eng - .spawn2(Phase::Present, data.clone().present_handler()), - _data: data, - }); + self.state.set_render_ctx(&self.ctx); - state.set_render_ctx(&ctx); - state.backend.set(Some(slf.clone())); - - Ok(slf) + pending().await } -} -impl XBackendData { async fn event_handler(self: Rc) { loop { let event = self.c.event().await; @@ -856,7 +859,7 @@ impl XBackendData { struct XOutput { id: ConnectorId, - _backend: Rc, + _backend: Rc, window: u32, events: SyncQueue, width: Cell, @@ -908,7 +911,7 @@ impl Connector for XOutput { struct XSeat { kb_id: InputDeviceId, mouse_id: InputDeviceId, - backend: Rc, + backend: Rc, kb: u16, mouse: u16, removed: Cell, diff --git a/src/cli.rs b/src/cli.rs index e8666a54..68f00ffb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -87,7 +87,7 @@ pub struct SetLogArgs { level: CliLogLevel, } -#[derive(ArgEnum, Debug, Copy, Clone, Hash)] +#[derive(ArgEnum, Debug, Copy, Clone, Hash, Eq, PartialEq)] pub enum CliBackend { X11, Metal, diff --git a/src/compositor.rs b/src/compositor.rs index 46cf9a06..bc3e732f 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -1,10 +1,10 @@ use { crate::{ acceptor::{Acceptor, AcceptorError}, - async_engine::{AsyncEngine, AsyncError, Phase}, - backend, - backends::dummy::DummyOutput, - cli::{GlobalArgs, RunArgs}, + async_engine::{AsyncEngine, AsyncError, Phase, SpawnedFuture}, + backend::{self, Backend}, + backends::{dummy::DummyOutput, metal, x}, + cli::{CliBackend, GlobalArgs, RunArgs}, client::Clients, clientmem::{self, ClientMemError}, config::ConfigProxy, @@ -29,13 +29,17 @@ use { }, wheel::{Wheel, WheelError}, xkbcommon::XkbContext, - xwayland, }, + ahash::AHashSet, forker::ForkerProxy, std::{cell::Cell, ops::Deref, rc::Rc, sync::Arc, time::Duration}, thiserror::Error, uapi::c, }; +use crate::backends::dummy::DummyBackend; +use crate::state::XWaylandState; +use crate::tasks::idle; +use crate::user_session::import_environment; pub const MAX_EXTENTS: i32 = (1 << 22) - 1; @@ -45,7 +49,7 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) { Err(e) => fatal!("Could not create a forker process: {}", ErrorFmt(e)), }; let logger = Logger::install_compositor(global.log_level.into()); - if let Err(e) = main_(forker, logger.clone(), &args) { + if let Err(e) = start_compositor2(forker, logger.clone(), args) { let e = ErrorFmt(e); log::error!("A fatal error occurred: {}", e); eprintln!("A fatal error occurred: {}", e); @@ -73,7 +77,14 @@ enum MainError { RenderError(#[from] RenderError), } -fn main_(forker: Rc, logger: Arc, _args: &RunArgs) -> Result<(), MainError> { +pub const WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY"; +pub const DISPLAY: &str = "DISPLAY"; + +fn start_compositor2( + forker: Rc, + logger: Arc, + run_args: RunArgs, +) -> Result<(), MainError> { init_fd_limit(); leaks::init(); render::init()?; @@ -88,7 +99,7 @@ fn main_(forker: Rc, logger: Arc, _args: &RunArgs) -> Resul let node_ids = NodeIds::default(); let state = Rc::new(State { xkb_ctx, - backend: Default::default(), + backend: CloneCell::new(Rc::new(DummyBackend)), forker: Default::default(), default_keymap: xkb_keymap, eng: engine.clone(), @@ -126,77 +137,23 @@ fn main_(forker: Rc, logger: Arc, _args: &RunArgs) -> Resul idle: IdleState { input: Default::default(), change: Default::default(), - timeout: Cell::new(Duration::from_secs(10)), + timeout: Cell::new(Duration::from_secs(10 * 60)), timeout_changed: Default::default(), }, + run_args, + xwayland: XWaylandState { + enabled: Cell::new(true), + handler: Default::default(), + }, + socket_path: Default::default(), }); - { - let dummy_output = Rc::new(OutputNode { - id: state.node_ids.next(), - global: Rc::new(WlOutputGlobal::new( - state.globals.name(), - &Rc::new(ConnectorData { - connector: Rc::new(DummyOutput { - id: state.connector_ids.next(), - }), - handler: Cell::new(None), - connected: Cell::new(true), - name: "Dummy".to_string(), - }), - 0, - &backend::Mode { - width: 0, - height: 0, - refresh_rate_millihz: 0, - }, - "none", - "none", - 0, - 0, - )), - workspaces: Default::default(), - workspace: Default::default(), - seat_state: Default::default(), - layers: Default::default(), - render_data: Default::default(), - state: state.clone(), - is_dummy: true, - status: Default::default(), - }); - let dummy_workspace = Rc::new(WorkspaceNode { - id: state.node_ids.next(), - output: CloneCell::new(dummy_output.clone()), - position: Default::default(), - container: Default::default(), - stacked: Default::default(), - seat_state: Default::default(), - name: "dummy".to_string(), - output_link: Default::default(), - visible: Cell::new(false), - }); - dummy_workspace.output_link.set(Some( - dummy_output.workspaces.add_last(dummy_workspace.clone()), - )); - dummy_output.show_workspace(&dummy_workspace); - state.dummy_output.set(Some(dummy_output)); - } - forker.install(&state); - let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); - let _slow_client_handler = engine.spawn(tasks::handle_slow_clients(state.clone())); - let _container_do_layout = engine.spawn2(Phase::Layout, container_layout(state.clone())); - let _container_render_titles = - engine.spawn2(Phase::PostLayout, container_render_data(state.clone())); - let _float_do_layout = engine.spawn2(Phase::Layout, float_layout(state.clone())); - let _float_render_titles = engine.spawn2(Phase::PostLayout, float_titles(state.clone())); + create_dummy_output(&state); let socket_path = Acceptor::install(&state)?; - forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes()); + forker.install(&state); + forker.setenv(WAYLAND_DISPLAY.as_bytes(), socket_path.as_bytes()); forker.setenv(b"_JAVA_AWT_WM_NONREPARENTING", b"1"); - let _xwayland = engine.spawn(xwayland::manage(state.clone())); - let _backend = engine.spawn(tasks::start_backend(state.clone())); - let config = ConfigProxy::default(&state); - state.config.set(Some(Rc::new(config))); + let _compositor = engine.spawn(start_compositor3(state.clone())); el.run()?; - drop(_xwayland); state.clients.clear(); for (_, seat) in state.globals.seats.lock().deref() { seat.clear(); @@ -205,6 +162,83 @@ fn main_(forker: Rc, logger: Arc, _args: &RunArgs) -> Resul Ok(()) } +async fn start_compositor3(state: Rc) { + let backend = match create_backend(&state).await { + Some(b) => b, + _ => { + log::error!("Could not create a backend"); + state.el.stop(); + return; + } + }; + state.backend.set(backend.clone()); + + if backend.is_freestanding() { + import_environment(&state, WAYLAND_DISPLAY, &state.socket_path.get()); + } + + let config = ConfigProxy::default(&state); + state.config.set(Some(Rc::new(config))); + + let _geh = start_global_event_handlers(&state, &backend); + state.start_xwayland(); + + match backend.run().await { + Err(e) => log::error!("Backend failed: {}", ErrorFmt(e.deref())), + _ => log::error!("Backend stopped without an error"), + } + state.el.stop(); +} + +fn start_global_event_handlers(state: &Rc, backend: &Rc) -> Vec> { + let eng = &state.eng; + let mut res = vec![]; + + res.push(eng.spawn(tasks::handle_backend_events(state.clone()))); + res.push(eng.spawn(tasks::handle_slow_clients(state.clone()))); + res.push(eng.spawn2(Phase::Layout, container_layout(state.clone()))); + res.push(eng.spawn2(Phase::PostLayout, container_render_data(state.clone()))); + res.push(eng.spawn2(Phase::Layout, float_layout(state.clone()))); + res.push(eng.spawn2(Phase::PostLayout, float_titles(state.clone()))); + res.push(eng.spawn2(Phase::PostLayout, idle(state.clone(), backend.clone()))); + + res +} + +async fn create_backend(state: &Rc) -> Option> { + let mut backends = &state.run_args.backends[..]; + if backends.is_empty() { + backends = &[CliBackend::X11, CliBackend::Metal]; + } + let mut tried_backends = AHashSet::new(); + for &backend in backends { + if !tried_backends.insert(backend) { + continue; + } + match backend { + CliBackend::X11 => { + log::info!("Trying to create X backend"); + match x::create(state).await { + Ok(b) => return Some(b), + Err(e) => { + log::error!("Could not create X backend: {}", ErrorFmt(e)); + } + } + } + CliBackend::Metal => { + log::info!("Trying to create metal backend"); + match metal::create(state).await { + Ok(b) => return Some(b), + Err(e) => { + log::error!("Could not create metal backend: {}", ErrorFmt(e)); + } + } + } + } + } + None +} + fn init_fd_limit() { let res = OsError::tri(|| { let mut cur = uapi::getrlimit(c::RLIMIT_NOFILE as _)?; @@ -223,3 +257,54 @@ fn init_fd_limit() { log::warn!("Could not increase file descriptor limit: {}", ErrorFmt(e)); } } + +fn create_dummy_output(state: &Rc) { + let dummy_output = Rc::new(OutputNode { + id: state.node_ids.next(), + global: Rc::new(WlOutputGlobal::new( + state.globals.name(), + &Rc::new(ConnectorData { + connector: Rc::new(DummyOutput { + id: state.connector_ids.next(), + }), + handler: Cell::new(None), + connected: Cell::new(true), + name: "Dummy".to_string(), + }), + 0, + &backend::Mode { + width: 0, + height: 0, + refresh_rate_millihz: 0, + }, + "none", + "none", + 0, + 0, + )), + workspaces: Default::default(), + workspace: Default::default(), + seat_state: Default::default(), + layers: Default::default(), + render_data: Default::default(), + state: state.clone(), + is_dummy: true, + status: Default::default(), + }); + let dummy_workspace = Rc::new(WorkspaceNode { + id: state.node_ids.next(), + output: CloneCell::new(dummy_output.clone()), + position: Default::default(), + container: Default::default(), + stacked: Default::default(), + seat_state: Default::default(), + name: "dummy".to_string(), + output_link: Default::default(), + visible: Cell::new(false), + }); + dummy_workspace.output_link.set(Some( + dummy_output.workspaces.add_last(dummy_workspace.clone()), + )); + dummy_output.show_workspace(&dummy_workspace); + state.dummy_output.set(Some(dummy_output)); +} diff --git a/src/config/handler.rs b/src/config/handler.rs index 5ccfdf7e..8da75ca5 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -172,6 +172,12 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_set_env(&self, key: &str, val: &str) { + if let Some(f) = self.state.forker.get() { + f.setenv(key.as_bytes(), val.as_bytes()); + } + } + fn handle_program_timer( &self, timer: jay_config::Timer, @@ -654,10 +660,7 @@ impl ConfigProxyHandler { } fn handle_switch_to(&self, vtnr: u32) { - match self.state.backend.get() { - Some(b) => b.switch_to(vtnr), - _ => log::warn!("Cannot switch to VT {}: Backend has not yet started", vtnr), - } + self.state.backend.get().switch_to(vtnr); } fn handle_toggle_floating(&self, seat: Seat) -> Result<(), CphError> { @@ -877,6 +880,7 @@ impl ConfigProxyHandler { } => self .handle_program_timer(timer, initial, periodic) .wrn("program_timer")?, + ClientMessage::SetEnv { key, val } => self.handle_set_env(key, val), } Ok(()) } diff --git a/src/dbus.rs b/src/dbus.rs index eea76d04..8f2fbc56 100644 --- a/src/dbus.rs +++ b/src/dbus.rs @@ -30,6 +30,7 @@ use { thiserror::Error, uapi::OwnedFd, }; +use crate::utils::xrd::{xrd, XRD}; mod auth; mod dynamic_type; @@ -108,6 +109,8 @@ pub enum DbusError { InvalidBoolValue, #[error("Signature is empty")] EmptySignature, + #[error("The session bus address is not set")] + SessionBusAddressNotSet, #[error("Server does not support FD passing")] UnixFd, #[error("Server message has a different endianess than ourselves")] @@ -128,13 +131,25 @@ efrom!(DbusError, AsyncError); pub struct Dbus { eng: Rc, system: Rc, + session: Rc, + user_path: Option, } impl Dbus { pub fn new(eng: &Rc, run_toplevel: &Rc) -> Self { + let user_path = match xrd() { + Some(path) => Some(format!("{}/bus", path)), + _ => { + log::warn!("{} is not set", XRD); + None + } + }; + log::info!("dbus path = {:?}", user_path); Self { eng: eng.clone(), system: Rc::new(DbusHolder::new(run_toplevel)), + session: Rc::new(DbusHolder::new(run_toplevel)), + user_path, } } @@ -142,6 +157,15 @@ impl Dbus { self.system .get(&self.eng, "/var/run/dbus/system_bus_socket", "System bus") } + + pub fn session(&self) -> Result, DbusError> { + let sba = match self.user_path.as_deref() { + None => return Err(DbusError::SessionBusAddressNotSet), + Some(sba) => sba, + }; + self.session + .get(&self.eng, sba, "Session bus") + } } unsafe trait ReplyHandler { @@ -211,7 +235,7 @@ const NO_AUTO_START: u8 = 0x2; const ALLOW_INTERACTIVE_AUTHORIZATION: u8 = 0x4; pub const BUS_DEST: &str = "org.freedesktop.DBus"; -pub const BUS_PATH: &str = "/org/freedesktop/dbus"; +pub const BUS_PATH: &str = "/org/freedesktop/DBus"; #[derive(Default, Debug)] struct Headers<'a> { diff --git a/src/forker.rs b/src/forker.rs index fbe2d8f5..31a54b87 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -35,6 +35,7 @@ use { thiserror::Error, uapi::{c, pipe2, Errno, Fd, IntoUstr, OwnedFd, UstrPtr}, }; +use crate::compositor::{DISPLAY, WAYLAND_DISPLAY}; pub struct ForkerProxy { pidfd: Rc, @@ -297,8 +298,8 @@ struct Forker { impl Forker { fn handle(ppid: c::pid_t, socket: OwnedFd) -> ! { env::set_var("XDG_SESSION_TYPE", "wayland"); - env::remove_var("DISPLAY"); - env::remove_var("WAYLAND_DISPLAY"); + env::remove_var(DISPLAY); + env::remove_var(WAYLAND_DISPLAY); setup_name("the ol' forker"); setup_deathsig(ppid); reset_signals(); diff --git a/src/main.rs b/src/main.rs index 3e1b5e37..07c4f5c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,6 +78,7 @@ mod wire_xcon; mod xcon; mod xkbcommon; mod xwayland; +mod user_session; fn main() { cli::main(); diff --git a/src/state.rs b/src/state.rs index 3a31d96a..10281594 100644 --- a/src/state.rs +++ b/src/state.rs @@ -5,6 +5,7 @@ use { Backend, BackendEvent, Connector, ConnectorId, ConnectorIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo, }, + cli::RunArgs, client::{Client, Clients}, config::ConfigProxy, cursor::ServerCursors, @@ -30,6 +31,7 @@ use { }, wheel::Wheel, xkbcommon::{XkbContext, XkbKeymap}, + xwayland, }, ahash::AHashMap, jay_config::Direction, @@ -43,7 +45,7 @@ use { pub struct State { pub xkb_ctx: XkbContext, - pub backend: CloneCell>>, + pub backend: CloneCell>, pub forker: CloneCell>>, pub default_keymap: Rc, pub eng: Rc, @@ -79,6 +81,14 @@ pub struct State { pub outputs: CopyHashMap>, pub status: CloneCell>, pub idle: IdleState, + pub run_args: RunArgs, + pub xwayland: XWaylandState, + pub socket_path: CloneCell>, +} + +pub struct XWaylandState { + pub enabled: Cell, + pub handler: RefCell>>, } pub struct IdleState { @@ -305,4 +315,14 @@ impl State { self.idle.change.trigger(); } } + + pub fn start_xwayland(self: &Rc) { + if !self.xwayland.enabled.get() { + return; + } + let mut handler = self.xwayland.handler.borrow_mut(); + if handler.is_none() { + *handler = Some(self.eng.spawn(xwayland::manage(self.clone()))); + } + } } diff --git a/src/tasks.rs b/src/tasks.rs index b467faf7..00042607 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -3,8 +3,8 @@ mod connector; mod idle; mod input_device; mod slow_clients; -mod start_backend; +pub use idle::idle; use { crate::{ state::State, @@ -12,7 +12,6 @@ use { }, std::rc::Rc, }; -pub use {idle::idle, start_backend::start_backend}; pub async fn handle_backend_events(state: Rc) { let mut beh = BackendEventHandler { state }; diff --git a/src/tasks/idle.rs b/src/tasks/idle.rs index 8c6f0f77..97c6eabc 100644 --- a/src/tasks/idle.rs +++ b/src/tasks/idle.rs @@ -11,6 +11,9 @@ use { }; pub async fn idle(state: Rc, backend: Rc) { + if !backend.supports_idle() { + return; + } let timer = match state.eng.timer(c::CLOCK_MONOTONIC) { Ok(t) => t, Err(e) => { diff --git a/src/tasks/start_backend.rs b/src/tasks/start_backend.rs deleted file mode 100644 index e819dd93..00000000 --- a/src/tasks/start_backend.rs +++ /dev/null @@ -1,29 +0,0 @@ -use { - crate::{ - backends::{metal, x::XBackend}, - state::State, - utils::errorfmt::ErrorFmt, - }, - std::{future::pending, rc::Rc}, -}; - -pub async fn start_backend(state: Rc) { - log::info!("Trying to start X backend"); - // let e = match XorgBackend::new(&state) { - // Ok(b) => { - // state.backend.set(Some(b)); - // pending().await - // } - // Err(e) => e, - // }; - let e = match XBackend::run(&state).await { - Ok(_) => pending().await, - Err(e) => e, - }; - log::warn!("Could not start X backend: {}", ErrorFmt(e)); - log::info!("Trying to start metal backend"); - let e = metal::run(state.clone()).await; - log::error!("Metal backend failed: {}", ErrorFmt(e)); - log::warn!("Shutting down"); - state.el.stop(); -} diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index 553b5d22..0c20991b 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -38,6 +38,8 @@ use { thiserror::Error, uapi::{c, format_ustr}, }; +use crate::compositor::WAYLAND_DISPLAY; +use crate::utils::xrd::xrd; #[derive(Debug, Error)] pub enum ToolClientError { @@ -133,11 +135,11 @@ impl ToolClient { 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 xrd = match xrd() { + Some(d) => d, + _ => return Err(ToolClientError::XrdNotSet), }; - let wd = match std::env::var("WAYLAND_DISPLAY") { + let wd = match std::env::var(WAYLAND_DISPLAY) { Ok(d) => d, Err(_) => return Err(ToolClientError::WaylandDisplayNotSet), }; diff --git a/src/user_session.rs b/src/user_session.rs new file mode 100644 index 00000000..6722e578 --- /dev/null +++ b/src/user_session.rs @@ -0,0 +1,54 @@ +use std::borrow::Cow; +use std::rc::Rc; +use thiserror::Error; +use crate::dbus::{BUS_DEST, BUS_PATH, DbusError, DictEntry}; +use crate::state::State; +use crate::utils::errorfmt::ErrorFmt; +use crate::wire_dbus::org; + +const SYSTEMD_DEST: &str = "org.freedesktop.systemd1"; +const SYSTEMD_PATH: &str = "/org/freedesktop/systemd1"; + +#[derive(Debug, Error)] +pub enum UserSessionError { + #[error("Could not access the user session bus")] + AcquireSessionBus(#[source] DbusError), +} + +pub fn import_environment(state: &Rc, key: &str, value: &str) { + if let Err(e) = import_environment_(state, key, value) { + log::error!("Could not import `{}={}` into the system environment: {}", key, value, ErrorFmt(e)); + } +} + +fn import_environment_(state: &Rc, key: &str, value: &str) -> Result<(), UserSessionError> { + let session = match state.dbus.session() { + Ok(s) => s, + Err(e) => return Err(UserSessionError::AcquireSessionBus(e)), + }; + let setting = format!("{}={}", key, value); + session.call(BUS_DEST, BUS_PATH, org::freedesktop::dbus::UpdateActivationEnvironment { + environment: Cow::Borrowed(&[DictEntry { + key: key.into(), + value: value.into(), + }]) + }, { + let setting = setting.clone(); + move |rep| { + if let Err(e) = rep { + log::error!("Could not import `{}` into the dbus environment: {}", setting, ErrorFmt(e)); + } + } + }); + session.call(SYSTEMD_DEST, SYSTEMD_PATH, org::freedesktop::systemd1::manager::SetEnvironment { + names: Cow::Borrowed(&[Cow::Borrowed(&setting)]), + }, { + let setting = setting.clone(); + move |rep| { + if let Err(e) = rep { + log::error!("Could not import `{}` into the systemd environment: {}", setting, ErrorFmt(e)); + } + } + }); + Ok(()) +} diff --git a/src/utils.rs b/src/utils.rs index 45a42bc8..58ff2993 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -27,3 +27,4 @@ pub mod vasprintf; pub mod vec_ext; pub mod vecstorage; pub mod windows; +pub mod xrd; diff --git a/src/utils/xrd.rs b/src/utils/xrd.rs new file mode 100644 index 00000000..ca355c99 --- /dev/null +++ b/src/utils/xrd.rs @@ -0,0 +1,5 @@ +pub const XRD: &str = "XDG_RUNTIME_DIR"; + +pub fn xrd() -> Option { + std::env::var(XRD).ok() +} diff --git a/src/xcon.rs b/src/xcon.rs index d8f5e218..a95d367a 100644 --- a/src/xcon.rs +++ b/src/xcon.rs @@ -47,6 +47,7 @@ use { thiserror::Error, uapi::{c, OwnedFd}, }; +use crate::compositor::DISPLAY; pub mod consts; mod formatter; @@ -885,7 +886,7 @@ impl XconData { } fn parse_display() -> Result { - let display = match std::env::var("DISPLAY") { + let display = match std::env::var(DISPLAY) { Ok(d) => d, _ => return Err(XconError::DisplayNotSet), }; diff --git a/src/xwayland.rs b/src/xwayland.rs index 24d04f0a..3355899b 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -21,6 +21,8 @@ use { thiserror::Error, uapi::{c, pipe2, Errno, OwnedFd}, }; +use crate::compositor::DISPLAY; +use crate::user_session::import_environment; #[derive(Debug, Error)] enum XWaylandError { @@ -92,10 +94,15 @@ pub async fn manage(state: Rc) { }; if let Err(e) = uapi::listen(socket.raw(), 4096) { log::error!("Could not listen on the Xwayland socket: {}", ErrorFmt(e)); + return; } - forker.setenv(b"DISPLAY", format!(":{}", xsocket.id).as_bytes()); + let display = format!(":{}", xsocket.id); + forker.setenv(DISPLAY.as_bytes(), display.as_bytes()); log::info!("Allocated display :{} for Xwayland", xsocket.id); log::info!("Waiting for connection attempt"); + if state.backend.get().is_freestanding() { + import_environment(&state, DISPLAY, &display); + } let res = XWaylandError::tria(async { state.eng.fd(&socket)?.readable().await?; Ok(()) @@ -111,7 +118,7 @@ pub async fn manage(state: Rc) { } else { log::warn!("Xwayland exited unexpectedly"); } - forker.unsetenv(b"DISPLAY"); + forker.unsetenv(DISPLAY.as_bytes()); } } diff --git a/wire-dbus/org.freedesktop.systemd1.Manager.txt b/wire-dbus/org.freedesktop.systemd1.Manager.txt new file mode 100644 index 00000000..2623e5fc --- /dev/null +++ b/wire-dbus/org.freedesktop.systemd1.Manager.txt @@ -0,0 +1,4 @@ +fn SetEnvironment( + names: array(string), +) { +}