diff --git a/src/backends/x.rs b/src/backends/x.rs index 416da12c..58cca93d 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -618,7 +618,13 @@ impl XBackendData { } } if !matched_any { - fatal!("idle event did not match any images {:#?}", event); + fatal!( + "idle event did not match any images {}, {}, {}, {:#?}", + output.serial.get(), + output.images[0].last_serial.get(), + output.images[1].last_serial.get(), + event + ); } Ok(()) } diff --git a/src/cli.rs b/src/cli.rs index 5f43c231..1b93ab60 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,6 @@ mod generate; mod log; +mod quit; use crate::compositor::start_compositor; use ::log::Level; @@ -30,6 +31,8 @@ pub enum Cmd { GenerateCompletion(GenerateArgs), /// Open the log file. Log(LogArgs), + /// Stop the compositor. + Quit, } #[derive(Args, Debug)] @@ -104,5 +107,6 @@ pub fn main() { Cmd::Run(a) => start_compositor(cli.global, a), Cmd::GenerateCompletion(g) => generate::main(g), Cmd::Log(a) => log::main(cli.global, a), + Cmd::Quit => quit::main(cli.global), } } diff --git a/src/cli/log.rs b/src/cli/log.rs index 277e5158..3c122992 100644 --- a/src/cli/log.rs +++ b/src/cli/log.rs @@ -1,28 +1,21 @@ 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 crate::wire::{jay_compositor, jay_log_file}; use bstr::{BString, ByteSlice}; use jay_compositor::GetLogFile; use jay_log_file::Path; -use std::cell::{Cell, RefCell}; +use std::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, }); @@ -31,41 +24,13 @@ pub fn main(global: GlobalArgs, args: LogArgs) { struct Log { tc: Rc, - registry: Cell, - comp: Cell, path: RefCell>, args: LogArgs, } async fn run(log: Rc) { 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 comp = tc.jay_compositor().await; let log_file = tc.id(); tc.send(GetLogFile { self_id: comp, diff --git a/src/cli/quit.rs b/src/cli/quit.rs new file mode 100644 index 00000000..c5a699bd --- /dev/null +++ b/src/cli/quit.rs @@ -0,0 +1,15 @@ +use crate::cli::GlobalArgs; +use crate::tools::tool_client::ToolClient; +use crate::wire::jay_compositor::Quit; +use std::rc::Rc; + +pub fn main(global: GlobalArgs) { + let tc = ToolClient::new(global.log_level.into()); + tc.run(run(tc.clone())); +} + +async fn run(tc: Rc) { + let comp = tc.jay_compositor().await; + tc.send(Quit { self_id: comp }); + tc.round_trip().await; +} diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index bdb6117a..c3e99066 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -74,6 +74,13 @@ impl JayCompositor { log_file.send_path(self.client.state.logger.path()); Ok(()) } + + fn quit(&self, parser: MsgParser<'_, '_>) -> Result<(), QuitError> { + let _req: Quit = self.client.parse(self, parser)?; + log::info!("Quitting"); + self.client.state.el.stop(); + Ok(()) + } } object_base! { @@ -81,11 +88,12 @@ object_base! { DESTROY => destroy, GET_LOG_FILE => get_log_file, + QUIT => quit, } impl Object for JayCompositor { fn num_requests(&self) -> u32 { - GET_LOG_FILE + 1 + QUIT + 1 } } @@ -97,6 +105,8 @@ pub enum JayCompositorError { DestroyError(#[from] DestroyError), #[error("Could not process a `get_log_file` request")] GetLogFileError(#[from] GetLogFileError), + #[error("Could not process a `quit` request")] + QuitError(#[from] QuitError), #[error(transparent)] ClientError(Box), } @@ -121,3 +131,10 @@ pub enum GetLogFileError { } efrom!(GetLogFileError, ClientError); efrom!(GetLogFileError, MsgParserError); + +#[derive(Debug, Error)] +pub enum QuitError { + #[error("Parsing failed")] + MsgParserError(#[source] Box), +} +efrom!(QuitError, MsgParserError); diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index d5f547bf..93c4cd73 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -9,13 +9,17 @@ use crate::utils::buffd::{ BufFdError, BufFdIn, BufFdOut, MsgFormatter, MsgParser, MsgParserError, OutBuffer, OutBufferSwapchain, }; +use crate::utils::clonecell::CloneCell; 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 crate::wire::{ + wl_callback, wl_display, wl_registry, JayCompositor, JayCompositorId, WlCallbackId, + WlRegistryId, +}; use ahash::AHashMap; use log::Level; use std::cell::{Cell, RefCell}; @@ -80,6 +84,8 @@ pub struct ToolClient { next_id: NumCell, incoming: Cell>>, outgoing: Cell>>, + singletons: CloneCell>>, + jay_compositor: Cell>, } impl ToolClient { @@ -168,6 +174,8 @@ impl ToolClient { next_id: Default::default(), incoming: Default::default(), outgoing: Default::default(), + singletons: Default::default(), + jay_compositor: Default::default(), }); wl_display::Error::handle(&slf, WL_DISPLAY_ID, (), |_, val| { fatal!("The compositor returned a fatal error: {}", val.message); @@ -257,6 +265,64 @@ impl ToolClient { }); ah.triggered().await; } + + pub async fn singletons(self: &Rc) -> Rc { + if let Some(res) = self.singletons.get() { + return res; + } + #[derive(Default)] + struct S { + jay_compositor: Cell>, + } + let s = Rc::new(S::default()); + let registry: WlRegistryId = self.id(); + self.send(wl_display::GetRegistry { + self_id: WL_DISPLAY_ID, + registry, + }); + wl_registry::Global::handle(self, registry, s.clone(), |s, g| { + if g.interface == JayCompositor.name() { + s.jay_compositor.set(Some(g.name)); + } + }); + self.round_trip().await; + macro_rules! get { + ($field:ident, $if:expr) => { + match s.$field.get() { + Some(j) => j, + _ => fatal!("Compositor does not provide the {} singleton", $if.name()), + } + }; + } + let res = Rc::new(Singletons { + registry, + jay_compositor: get!(jay_compositor, JayCompositor), + }); + self.singletons.set(Some(res.clone())); + res + } + + pub async fn jay_compositor(self: &Rc) -> JayCompositorId { + if let Some(id) = self.jay_compositor.get() { + return id; + } + let s = self.singletons().await; + let id: JayCompositorId = self.id(); + self.send(wl_registry::Bind { + self_id: s.registry, + name: s.jay_compositor, + interface: JayCompositor.name(), + version: 1, + id: id.into(), + }); + self.jay_compositor.set(Some(id)); + id + } +} + +pub struct Singletons { + registry: WlRegistryId, + pub jay_compositor: u32, } pub const NONE_FUTURE: Option> = None; diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index f539733e..71cef375 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -7,3 +7,6 @@ msg destroy = 0 { msg get_log_file = 1 { id: id(jay_log_file), } + +msg quit = 2 { +}