1
0
Fork 0
forked from wry/wry

autocommit 2022-04-14 19:52:11 CEST

This commit is contained in:
Julian Orth 2022-04-14 19:52:11 +02:00
parent 35ddfbcbe3
commit 5f13954dbc
27 changed files with 556 additions and 312 deletions

View file

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

View file

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

View file

@ -221,6 +221,10 @@ pub enum ClientMessage<'a> {
initial: Option<Duration>,
periodic: Option<Duration>,
},
SetEnv {
key: &'a str,
val: &'a str,
},
}
#[derive(Encode, Decode, Debug)]

View file

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

View file

@ -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<AllocatedSocket, AcceptorError> {
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<AllocatedSocket, AcceptorError> {
}
impl Acceptor {
pub fn install(state: &Rc<State>) -> Result<Ustring, AcceptorError> {
pub fn install(state: &Rc<State>) -> Result<Rc<String>, 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)
}
}

View file

@ -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<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>>;
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)]

View file

@ -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<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>> {
unreachable!();
}
}
pub struct DummyOutput {
pub id: ConnectorId,

View file

@ -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<State>) -> 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<State>,
udev: Rc<Udev>,
monitor: Rc<UdevMonitor>,
@ -122,9 +117,30 @@ struct MetalBackend {
device_holder: Rc<DeviceHolder>,
session: Session,
drm_ids: DrmIds,
pause_handler: Cell<Option<SignalHandler>>,
resume_handler: Cell<Option<SignalHandler>>,
}
impl MetalBackend {
async fn run(self: Rc<Self>) -> 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<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>> {
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<State>) -> Result<(), MetalError> {
fn dup_async_fd(state: &Rc<State>, fd: c::c_int) -> Result<AsyncFd, MetalError> {
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<State>) -> Result<Rc<MetalBackend>, 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<State>) -> 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<State>) -> 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 {

View file

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

View file

@ -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<XBackendData>,
_events: SpawnedFuture<()>,
_present: SpawnedFuture<()>,
_grab: SpawnedFuture<()>,
pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, 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<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>> {
let slf = self.clone();
self.state.eng.spawn(async move {
slf.run().await?;
Ok(())
})
}
}
struct XBackendData {
pub struct XBackend {
state: Rc<State>,
c: Rc<Xcon>,
outputs: CopyHashMap<u32, Rc<XOutput>>,
@ -130,141 +253,21 @@ struct XBackendData {
}
impl XBackend {
pub async fn run(state: &Rc<State>) -> Result<Rc<Self>, 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<Self>) -> 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<Self>) {
loop {
let event = self.c.event().await;
@ -856,7 +859,7 @@ impl XBackendData {
struct XOutput {
id: ConnectorId,
_backend: Rc<XBackendData>,
_backend: Rc<XBackend>,
window: u32,
events: SyncQueue<ConnectorEvent>,
width: Cell<i32>,
@ -908,7 +911,7 @@ impl Connector for XOutput {
struct XSeat {
kb_id: InputDeviceId,
mouse_id: InputDeviceId,
backend: Rc<XBackendData>,
backend: Rc<XBackend>,
kb: u16,
mouse: u16,
removed: Cell<bool>,

View file

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

View file

@ -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<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
pub const WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY";
pub const DISPLAY: &str = "DISPLAY";
fn start_compositor2(
forker: Rc<ForkerProxy>,
logger: Arc<Logger>,
run_args: RunArgs,
) -> Result<(), MainError> {
init_fd_limit();
leaks::init();
render::init()?;
@ -88,7 +99,7 @@ fn main_(forker: Rc<ForkerProxy>, logger: Arc<Logger>, _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<ForkerProxy>, logger: Arc<Logger>, _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<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Resul
Ok(())
}
async fn start_compositor3(state: Rc<State>) {
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<State>, backend: &Rc<dyn Backend>) -> Vec<SpawnedFuture<()>> {
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<State>) -> Option<Rc<dyn Backend>> {
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<State>) {
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));
}

View file

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

View file

@ -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<AsyncEngine>,
system: Rc<DbusHolder>,
session: Rc<DbusHolder>,
user_path: Option<String>,
}
impl Dbus {
pub fn new(eng: &Rc<AsyncEngine>, run_toplevel: &Rc<RunToplevel>) -> 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<Rc<DbusSocket>, 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> {

View file

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

View file

@ -78,6 +78,7 @@ mod wire_xcon;
mod xcon;
mod xkbcommon;
mod xwayland;
mod user_session;
fn main() {
cli::main();

View file

@ -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<Option<Rc<dyn Backend>>>,
pub backend: CloneCell<Rc<dyn Backend>>,
pub forker: CloneCell<Option<Rc<ForkerProxy>>>,
pub default_keymap: Rc<XkbKeymap>,
pub eng: Rc<AsyncEngine>,
@ -79,6 +81,14 @@ pub struct State {
pub outputs: CopyHashMap<ConnectorId, Rc<OutputData>>,
pub status: CloneCell<Rc<String>>,
pub idle: IdleState,
pub run_args: RunArgs,
pub xwayland: XWaylandState,
pub socket_path: CloneCell<Rc<String>>,
}
pub struct XWaylandState {
pub enabled: Cell<bool>,
pub handler: RefCell<Option<SpawnedFuture<()>>>,
}
pub struct IdleState {
@ -305,4 +315,14 @@ impl State {
self.idle.change.trigger();
}
}
pub fn start_xwayland(self: &Rc<Self>) {
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())));
}
}
}

View file

@ -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<State>) {
let mut beh = BackendEventHandler { state };

View file

@ -11,6 +11,9 @@ use {
};
pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
if !backend.supports_idle() {
return;
}
let timer = match state.eng.timer(c::CLOCK_MONOTONIC) {
Ok(t) => t,
Err(e) => {

View file

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

View file

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

54
src/user_session.rs Normal file
View file

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

View file

@ -27,3 +27,4 @@ pub mod vasprintf;
pub mod vec_ext;
pub mod vecstorage;
pub mod windows;
pub mod xrd;

5
src/utils/xrd.rs Normal file
View file

@ -0,0 +1,5 @@
pub const XRD: &str = "XDG_RUNTIME_DIR";
pub fn xrd() -> Option<String> {
std::env::var(XRD).ok()
}

View file

@ -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<u32, XconError> {
let display = match std::env::var("DISPLAY") {
let display = match std::env::var(DISPLAY) {
Ok(d) => d,
_ => return Err(XconError::DisplayNotSet),
};

View file

@ -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<State>) {
};
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<State>) {
} else {
log::warn!("Xwayland exited unexpectedly");
}
forker.unsetenv(b"DISPLAY");
forker.unsetenv(DISPLAY.as_bytes());
}
}

View file

@ -0,0 +1,4 @@
fn SetEnvironment(
names: array(string),
) {
}