1
0
Fork 0
forked from wry/wry
wry/src/compositor.rs
2022-04-20 14:58:34 +02:00

334 lines
11 KiB
Rust

use {
crate::{
acceptor::{Acceptor, AcceptorError},
async_engine::{AsyncEngine, AsyncError, Phase, SpawnedFuture},
backend::{self, Backend},
backends::{
dummy::{DummyBackend, DummyOutput},
metal, x,
},
cli::{CliBackend, GlobalArgs, RunArgs},
client::Clients,
clientmem::{self, ClientMemError},
config::ConfigProxy,
dbus::Dbus,
event_loop::{EventLoop, EventLoopError},
forker,
globals::Globals,
ifs::{wl_output::WlOutputGlobal, wl_surface::NoneSurfaceExt},
leaks,
logger::Logger,
render::{self, RenderError},
sighand::{self, SighandError},
state::{ConnectorData, IdleState, State, XWaylandState},
tasks::{self, idle},
tree::{
container_layout, container_render_data, float_layout, float_titles, DisplayNode,
NodeIds, OutputNode, WorkspaceNode,
},
user_session::import_environment,
utils::{
clonecell::CloneCell, errorfmt::ErrorFmt, fdcloser::FdCloser, oserror::OsError,
queue::AsyncQueue, run_toplevel::RunToplevel, tri::Try,
},
wheel::{Wheel, WheelError},
xkbcommon::XkbContext,
},
ahash::AHashSet,
forker::ForkerProxy,
std::{cell::Cell, ops::Deref, rc::Rc, sync::Arc, time::Duration},
thiserror::Error,
uapi::c,
};
pub const MAX_EXTENTS: i32 = (1 << 22) - 1;
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
let forker = match ForkerProxy::create() {
Ok(f) => Rc::new(f),
Err(e) => fatal!("Could not create a forker process: {}", ErrorFmt(e)),
};
let logger = Logger::install_compositor(global.log_level.into());
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);
eprintln!("See {} for more details.", logger.path());
std::process::exit(1);
}
log::info!("Exit");
}
#[derive(Debug, Error)]
enum MainError {
#[error("The client acceptor caused an error")]
AcceptorError(#[from] AcceptorError),
#[error("The event loop caused an error")]
EventLoopError(#[from] EventLoopError),
#[error("The signal handler caused an error")]
SighandError(#[from] SighandError),
#[error("The clientmem subsystem caused an error")]
ClientmemError(#[from] ClientMemError),
#[error("The timer subsystem caused an error")]
WheelError(#[from] WheelError),
#[error("The async subsystem caused an error")]
AsyncError(#[from] AsyncError),
#[error("The render backend caused an error")]
RenderError(#[from] RenderError),
}
pub const WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY";
pub const DISPLAY: &str = "DISPLAY";
const STATIC_VARS: &[(&str, &str)] = &[
("XDG_CURRENT_DESKTOP", "jay"),
("XDG_SESSION_TYPE", "wayland"),
("_JAVA_AWT_WM_NONREPARENTING", "1"),
];
fn start_compositor2(
forker: Rc<ForkerProxy>,
logger: Arc<Logger>,
run_args: RunArgs,
) -> Result<(), MainError> {
log::info!("pid = {}", uapi::getpid());
init_fd_limit();
leaks::init();
render::init()?;
clientmem::init()?;
let el = EventLoop::new()?;
sighand::install(&el)?;
let xkb_ctx = XkbContext::new().unwrap();
let xkb_keymap = xkb_ctx.keymap_from_str(include_str!("keymap.xkb")).unwrap();
let wheel = Wheel::install(&el)?;
let engine = AsyncEngine::install(&el, &wheel)?;
let (_run_toplevel_future, run_toplevel) = RunToplevel::install(&engine);
let node_ids = NodeIds::default();
let state = Rc::new(State {
xkb_ctx,
backend: CloneCell::new(Rc::new(DummyBackend)),
forker: Default::default(),
default_keymap: xkb_keymap,
eng: engine.clone(),
el: el.clone(),
render_ctx: Default::default(),
cursors: Default::default(),
wheel,
clients: Clients::new(),
globals: Globals::new(),
connector_ids: Default::default(),
root: Rc::new(DisplayNode::new(node_ids.next())),
workspaces: Default::default(),
dummy_output: Default::default(),
node_ids,
backend_events: AsyncQueue::new(),
seat_ids: Default::default(),
seat_queue: Default::default(),
slow_clients: AsyncQueue::new(),
none_surface_ext: Rc::new(NoneSurfaceExt),
tree_changed_sent: Cell::new(false),
config: Default::default(),
input_device_ids: Default::default(),
input_device_handlers: Default::default(),
theme: Default::default(),
pending_container_layout: Default::default(),
pending_container_render_data: Default::default(),
pending_float_layout: Default::default(),
pending_float_titles: Default::default(),
dbus: Dbus::new(&engine, &run_toplevel),
fdcloser: FdCloser::new(),
logger,
connectors: Default::default(),
outputs: Default::default(),
status: Default::default(),
idle: IdleState {
input: Default::default(),
change: Default::default(),
timeout: Cell::new(Duration::from_secs(10 * 60)),
timeout_changed: Default::default(),
inhibitors: Default::default(),
inhibitors_changed: Default::default(),
},
run_args,
xwayland: XWaylandState {
enabled: Cell::new(true),
handler: Default::default(),
queue: Default::default(),
},
socket_path: Default::default(),
serial: Default::default(),
idle_inhibitor_ids: Default::default(),
run_toplevel,
});
create_dummy_output(&state);
let socket_path = Acceptor::install(&state)?;
forker.install(&state);
forker.setenv(WAYLAND_DISPLAY.as_bytes(), socket_path.as_bytes());
for (key, val) in STATIC_VARS {
forker.setenv(key.as_bytes(), val.as_bytes());
}
let _compositor = engine.spawn(start_compositor3(state.clone()));
el.run()?;
state.xwayland.handler.borrow_mut().take();
state.clients.clear();
for (_, seat) in state.globals.seats.lock().deref() {
seat.clear();
}
leaks::log_leaked();
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());
state.globals.add_singletons(&backend);
if backend.is_freestanding() {
import_environment(&state, WAYLAND_DISPLAY, &state.socket_path.get());
for (key, val) in STATIC_VARS {
import_environment(&state, key, val);
}
}
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 _)?;
if cur.rlim_cur < cur.rlim_max {
log::info!(
"Increasing file descriptor limit from {} to {}",
cur.rlim_cur,
cur.rlim_max
);
cur.rlim_cur = cur.rlim_max;
uapi::setrlimit(c::RLIMIT_NOFILE as _, &cur)?;
}
Ok(())
});
if let Err(e) = res {
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: Default::default(),
fullscreen: Default::default(),
});
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));
}