From a30306e3d5d81f456a99aab624354663f6106fcd Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 17 Apr 2022 17:08:31 +0200 Subject: [PATCH] autocommit 2022-04-17 17:08:31 CEST --- default-config/src/lib.rs | 5 + src/cli.rs | 48 ++++++ src/cli/idle.rs | 138 ++++++++++++++++++ src/client.rs | 9 +- src/compositor.rs | 1 + src/ifs.rs | 1 + src/ifs/ipc/wl_data_device.rs | 9 +- .../ipc/zwp_primary_selection_device_v1.rs | 10 +- src/ifs/jay_compositor.rs | 16 +- src/ifs/jay_idle.rs | 68 +++++++++ src/ifs/wl_compositor.rs | 4 +- src/ifs/wl_seat.rs | 3 +- src/ifs/wl_seat/event_handling.rs | 21 ++- src/ifs/wl_seat/kb_owner.rs | 4 + src/ifs/wl_seat/pointer_owner.rs | 7 +- src/ifs/wl_surface.rs | 4 + src/ifs/wl_surface/xwindow.rs | 22 +-- src/state.rs | 10 ++ src/tree.rs | 9 ++ src/xwayland.rs | 11 +- src/xwayland/xwm.rs | 10 +- wire/jay_compositor.txt | 4 + wire/jay_idle.txt | 18 +++ 23 files changed, 390 insertions(+), 42 deletions(-) create mode 100644 src/cli/idle.rs create mode 100644 src/ifs/jay_idle.rs create mode 100644 wire/jay_idle.txt diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index 40185538..5d6e4c74 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -27,6 +27,7 @@ use { }, std::time::Duration, }; +use jay_config::keyboard::syms::{SYM_a, SYM_e, SYM_o}; const MOD: Modifiers = ALT; @@ -77,6 +78,10 @@ fn configure_seat(s: Seat) { s.bind(MOD | sym, move || s.show_workspace(ws)); } + s.bind(MOD | SYM_a, || Command::new("spotify-remote").arg("a").spawn()); + s.bind(MOD | SYM_o, || Command::new("spotify-remote").arg("o").spawn()); + s.bind(MOD | SYM_e, || Command::new("spotify-remote").arg("e").spawn()); + fn do_grab(s: Seat, grab: bool) { for device in s.input_devices() { if device.has_capability(CAP_KEYBOARD) { diff --git a/src/cli.rs b/src/cli.rs index 68f00ffb..787e253f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,6 +3,7 @@ mod log; mod quit; mod screenshot; mod set_log_level; +mod idle; use { crate::compositor::start_compositor, @@ -41,6 +42,52 @@ pub enum Cmd { Quit, /// Take a screenshot. Screenshot(ScreenshotArgs), + /// Inspect/modify the idle (screensaver) settings. + Idle(IdleArgs), +} + +#[derive(Args, Debug)] +pub struct IdleArgs { + /// The filename of the saved screenshot + /// + /// If no filename is given, the screenshot will be saved under %Y-%m-%d-%H%M%S_jay.qoi + /// in the current directory. + /// + /// The filename can contain the usual strftime parameters. + #[clap(subcommand)] + pub command: Option, +} + +#[derive(Subcommand, Debug)] +pub enum IdleCmd { + /// Print the idle status. + Status, + /// Set the idle interval. + Set(IdleSetArgs), +} + +impl Default for IdleCmd { + fn default() -> Self { + Self::Status + } +} + +#[derive(Args, Debug)] +pub struct IdleSetArgs { + /// The interval of inactivity after which to disable the screens. + /// + /// This can be either a number in minutes and seconds or the keyword `disabled` to + /// disable the screensaver. + /// + /// Minutes and seconds can be specified in any of the following formats: + /// + /// * 1m + /// * 1m5s + /// * 1m 5s + /// * 1min 5sec + /// * 1 minute 5 seconds + #[clap(verbatim_doc_comment, required = true)] + pub interval: Vec, } #[derive(Args, Debug)] @@ -136,5 +183,6 @@ pub fn main() { Cmd::Quit => quit::main(cli.global), Cmd::SetLogLevel(a) => set_log_level::main(cli.global, a), Cmd::Screenshot(a) => screenshot::main(cli.global, a), + Cmd::Idle(a) => idle::main(cli.global, a), } } diff --git a/src/cli/idle.rs b/src/cli/idle.rs new file mode 100644 index 00000000..84080165 --- /dev/null +++ b/src/cli/idle.rs @@ -0,0 +1,138 @@ +use std::cell::Cell; +use std::collections::VecDeque; +use std::rc::Rc; +use std::str::FromStr; +use crate::cli::{GlobalArgs, IdleArgs, IdleCmd, IdleSetArgs}; +use crate::tools::tool_client::{Handle, ToolClient}; +use crate::utils::errorfmt::ErrorFmt; +use crate::wire::{jay_compositor, jay_idle, JayIdleId}; + +pub fn main(global: GlobalArgs, args: IdleArgs) { + let tc = ToolClient::new(global.log_level.into()); + let idle = Idle { + tc: tc.clone(), + }; + tc.run(idle.run(args)); +} + +struct Idle { + tc: Rc, +} + +impl Idle { + async fn run(self, args: IdleArgs) { + let tc = &self.tc; + let comp = tc.jay_compositor().await; + let idle = tc.id(); + tc.send(jay_compositor::GetIdle { + self_id: comp, + id: idle, + }); + match args.command.unwrap_or_default() { + IdleCmd::Status => self.status(idle).await, + IdleCmd::Set(args) => self.set(idle, args).await, + } + } + + async fn status(self, idle: JayIdleId) { + let tc = &self.tc; + tc.send(jay_idle::GetStatus { + self_id: idle, + }); + let interval = Rc::new(Cell::new(0u64)); + jay_idle::Interval::handle(tc, idle, interval.clone(), |iv, msg| { + iv.set(msg.interval); + }); + tc.round_trip().await; + let minutes = interval.get() / 60; + let seconds = interval.get() % 60; + if minutes == 0 && seconds == 0 { + println!("Interval: disabled"); + } else { + println!("Interval: {} minutes {} seconds", minutes, seconds); + } + } + + async fn set(self, idle: JayIdleId, args: IdleSetArgs) { + let tc = &self.tc; + let interval; + if args.interval.len() == 1 && args.interval[0] == "disabled" { + interval = 0; + } else { + let comp = parse_components(&args.interval); + let mut minutes = None; + let mut seconds = None; + let mut pending_num = None; + for comp in comp { + match comp { + Component::Number(_) if pending_num.is_some() => fatal!("missing number unit after {}", pending_num.unwrap()), + Component::Number(n) => pending_num = Some(n), + + Component::Minutes(n) if pending_num.is_none() => fatal!("`{}` must be preceded by a number", n), + Component::Minutes(_) if minutes.is_some() => fatal!("minutes specified multiple times"), + Component::Minutes(_) => minutes = pending_num.take(), + + Component::Seconds(n) if pending_num.is_none() => fatal!("`{}` must be preceded by a number", n), + Component::Seconds(_) if seconds.is_some() => fatal!("seconds specified multiple times"), + Component::Seconds(_) => seconds = pending_num.take(), + } + } + if pending_num.is_some() { + fatal!("missing number unit after {}", pending_num.unwrap()); + } + if minutes.is_none() && seconds.is_none() { + fatal!("minutes and/or numbers must be specified"); + } + interval = minutes.unwrap_or(0) * 60 + seconds.unwrap_or(0); + } + tc.send(jay_idle::SetInterval { + self_id: idle, + interval, + }); + tc.round_trip().await; + } +} + +#[derive(Debug)] +enum Component { + Number(u64), + Minutes(String), + Seconds(String), +} + +fn parse_components(args: &[String]) -> Vec { + let mut args = VecDeque::from_iter(args.iter().map(|s| s.to_ascii_lowercase())); + let mut res = vec![]; + while let Some(arg) = args.pop_front() { + if arg.is_empty() { + continue; + } + let mut arg = &arg[..]; + if is_num(arg.as_bytes()[0]) { + if let Some(pos) = arg.as_bytes().iter().position(|&a| !is_num(a)) { + args.push_front(arg[pos..].to_string()); + arg = &arg[..pos]; + } + match u64::from_str(arg) { + Ok(n) => res.push(Component::Number(n)), + Err(e) => fatal!("Could not parse `{}` as a number: {}", arg, ErrorFmt(e)), + } + } else { + if let Some(pos) = arg.as_bytes().iter().position(|&a| is_num(a)) { + args.push_front(arg[pos..].to_string()); + arg = &arg[..pos]; + } + let comp = match &arg[..] { + "minutes" | "minute" | "min" | "m" => Component::Minutes(arg.to_string()), + "seconds" | "second" | "sec" | "s" => Component::Seconds(arg.to_string()), + _ => fatal!("Could not parse `{}`", arg), + }; + res.push(comp); + } + } + res +} + +fn is_num(b: u8) -> bool { + matches!(b, b'0'..=b'9') +} diff --git a/src/client.rs b/src/client.rs index c90c1214..c06d7043 100644 --- a/src/client.rs +++ b/src/client.rs @@ -16,7 +16,6 @@ use { queue::AsyncQueue, }, wire::WlRegistryId, - xwayland::XWaylandEvent, }, ahash::AHashMap, std::{ @@ -100,7 +99,7 @@ impl Clients { } } }; - self.spawn2(id, global, socket, uid, pid, secure, None)?; + self.spawn2(id, global, socket, uid, pid, secure, false)?; Ok(()) } @@ -112,7 +111,7 @@ impl Clients { uid: c::uid_t, pid: c::pid_t, secure: bool, - xwayland_queue: Option>>, + is_xwayland: bool, ) -> Result, ClientError> { let data = Rc::new(Client { id, @@ -125,7 +124,7 @@ impl Clients { shutdown: Default::default(), dispatch_frame_requests: AsyncQueue::new(), tracker: Default::default(), - xwayland_queue, + is_xwayland, secure, last_serial: Cell::new(0), last_enter_serial: Cell::new(0), @@ -224,7 +223,7 @@ pub struct Client { shutdown: AsyncEvent, pub dispatch_frame_requests: AsyncQueue>, pub tracker: Tracker, - pub xwayland_queue: Option>>, + pub is_xwayland: bool, pub secure: bool, pub last_serial: Cell, pub last_enter_serial: Cell, diff --git a/src/compositor.rs b/src/compositor.rs index 40677dd8..5940d5bb 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -145,6 +145,7 @@ fn start_compositor2( xwayland: XWaylandState { enabled: Cell::new(true), handler: Default::default(), + queue: Default::default(), }, socket_path: Default::default(), serial: Default::default(), diff --git a/src/ifs.rs b/src/ifs.rs index 5260fc31..cde08a51 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -26,3 +26,4 @@ pub mod zxdg_decoration_manager_v1; pub mod zxdg_output_manager_v1; pub mod zxdg_output_v1; pub mod zxdg_toplevel_decoration_v1; +pub mod jay_idle; diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index fd022623..b3d3d61c 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -61,7 +61,14 @@ impl WlDataDevice { self.manager.client.event(Leave { self_id: self.id }) } - pub fn send_enter(&self, surface: WlSurfaceId, x: Fixed, y: Fixed, offer: WlDataOfferId, serial: u32) { + pub fn send_enter( + &self, + surface: WlSurfaceId, + x: Fixed, + y: Fixed, + offer: WlDataOfferId, + serial: u32, + ) { self.manager.client.event(Enter { self_id: self.id, serial, diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index 424a4cde..9d5766cd 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -64,7 +64,11 @@ impl ZwpPrimarySelectionDeviceV1 { fn set_selection(&self, parser: MsgParser<'_, '_>) -> Result<(), SetSelectionError> { let req: SetSelection = self.manager.client.parse(self, parser)?; self.seat.client.validate_serial(req.serial)?; - if !self.seat.global.may_modify_primary_selection(&self.seat.client, req.serial) { + if !self + .seat + .global + .may_modify_primary_selection(&self.seat.client, req.serial) + { log::warn!("Ignoring disallowed set_selection request"); return Ok(()); } @@ -73,7 +77,9 @@ impl ZwpPrimarySelectionDeviceV1 { } else { Some(self.manager.client.lookup(req.source)?) }; - self.seat.global.set_primary_selection(src, Some(req.serial))?; + self.seat + .global + .set_primary_selection(src, Some(req.serial))?; Ok(()) } diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index 50efdfae..3f4f0ffc 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -17,6 +17,7 @@ use { std::{ops::Deref, rc::Rc}, thiserror::Error, }; +use crate::ifs::jay_idle::JayIdle; pub struct JayCompositorGlobal { name: GlobalName, @@ -140,6 +141,18 @@ impl JayCompositor { self.client.remove_obj(ss.deref())?; Ok(()) } + + fn get_idle(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> { + let req: GetIdle = self.client.parse(self, parser)?; + let idle = Rc::new(JayIdle { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + }); + track!(self.client, idle); + self.client.add_client_obj(&idle)?; + Ok(()) + } } object_base2! { @@ -150,11 +163,12 @@ object_base2! { QUIT => quit, SET_LOG_LEVEL => set_log_level, TAKE_SCREENSHOT => take_screenshot, + GET_IDLE => get_idle, } impl Object for JayCompositor { fn num_requests(&self) -> u32 { - TAKE_SCREENSHOT + 1 + GET_IDLE + 1 } } diff --git a/src/ifs/jay_idle.rs b/src/ifs/jay_idle.rs new file mode 100644 index 00000000..7e6d3716 --- /dev/null +++ b/src/ifs/jay_idle.rs @@ -0,0 +1,68 @@ +use std::time::Duration; +use thiserror::Error; +use { + crate::{ + client::Client, + leaks::Tracker, + object::Object, + wire::{jay_idle::*}, + }, + std::rc::Rc, +}; +use crate::client::ClientError; +use crate::utils::buffd::{MsgParser, MsgParserError}; +use crate::wire::JayIdleId; + +pub struct JayIdle { + pub id: JayIdleId, + pub client: Rc, + pub tracker: Tracker, +} + +impl JayIdle { + fn send_interval(&self) { + let to = self.client.state.idle.timeout.get(); + self.client.event(Interval { + self_id: self.id, + interval: to.as_secs(), + }); + } + + fn get_status(&self, parser: MsgParser<'_, '_>) -> Result<(), JayIdleError> { + let _req: GetStatus = self.client.parse(self, parser)?; + self.send_interval(); + Ok(()) + } + + fn set_interval(&self, parser: MsgParser<'_, '_>) -> Result<(), JayIdleError> { + let req: SetInterval = self.client.parse(self, parser)?; + let interval = Duration::from_secs(req.interval); + self.client.state.idle.set_timeout(interval); + Ok(()) + } +} + +object_base2! { + JayIdle; + + GET_STATUS => get_status, + SET_INTERVAL => set_interval, +} + +impl Object for JayIdle { + fn num_requests(&self) -> u32 { + SET_INTERVAL + 1 + } +} + +simple_add_obj!(JayIdle); + +#[derive(Debug, Error)] +pub enum JayIdleError { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(JayIdleError, ClientError); +efrom!(JayIdleError, MsgParserError); diff --git a/src/ifs/wl_compositor.rs b/src/ifs/wl_compositor.rs index cc265aad..f6490270 100644 --- a/src/ifs/wl_compositor.rs +++ b/src/ifs/wl_compositor.rs @@ -53,8 +53,8 @@ impl WlCompositor { let surface = Rc::new(WlSurface::new(surface.id, &self.client)); track!(self.client, surface); self.client.add_client_obj(&surface)?; - if let Some(queue) = &self.client.xwayland_queue { - queue.push(XWaylandEvent::SurfaceCreated(surface.clone())); + if self.client.is_xwayland { + self.client.state.xwayland.queue.push(XWaylandEvent::SurfaceCreated(surface.clone())); } Ok(()) } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 10e24207..51ba71e6 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -336,7 +336,8 @@ impl WlSeatGlobal { icon: Option>, serial: u32, ) -> Result<(), WlSeatError> { - self.pointer_owner.start_drag(self, origin, source, icon, serial) + self.pointer_owner + .start_drag(self, origin, source, icon, serial) } pub fn cancel_dnd(self: &Rc) { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 02380a13..7e39025e 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -35,7 +35,7 @@ use { pub struct NodeSeatState { pointer_foci: SmallMap, 1>, kb_foci: SmallMap, 1>, - grabs: SmallMap, 1>, + pointer_grabs: SmallMap, 1>, dnd_targets: SmallMap, 1>, } @@ -59,11 +59,11 @@ impl NodeSeatState { } pub(super) fn add_pointer_grab(&self, seat: &Rc) { - self.grabs.insert(seat.id, seat.clone()); + self.pointer_grabs.insert(seat.id, seat.clone()); } pub(super) fn remove_pointer_grab(&self, seat: &WlSeatGlobal) { - self.grabs.remove(&seat.id); + self.pointer_grabs.remove(&seat.id); } pub(super) fn add_dnd_target(&self, seat: &Rc) { @@ -89,10 +89,10 @@ impl NodeSeatState { } fn release_kb_focus2(&self, focus_last: bool) { + self.release_kb_grab(); while let Some((_, seat)) = self.kb_foci.pop() { - seat.ungrab_kb(); seat.keyboard_node.set(seat.state.root.clone()); - log::info!("keyboard_node = root"); + // log::info!("keyboard_node = root"); if focus_last { if let Some(tl) = seat.toplevel_focus_history.last() { seat.focus_node(tl.focus_surface(seat.id)); @@ -116,7 +116,7 @@ impl NodeSeatState { fn destroy_node2(&self, node: &dyn Node, focus_last: bool) { // NOTE: Also called by set_visible(false) - while let Some((_, seat)) = self.grabs.pop() { + while let Some((_, seat)) = self.pointer_grabs.pop() { seat.pointer_owner.revert_to_default(&seat); } let node_id = node.node_id(); @@ -631,7 +631,14 @@ impl WlSeatGlobal { surface.client.flush(); } - pub fn dnd_surface_enter(&self, surface: &WlSurface, dnd: &Dnd, x: Fixed, y: Fixed, serial: u32) { + pub fn dnd_surface_enter( + &self, + surface: &WlSurface, + dnd: &Dnd, + x: Fixed, + y: Fixed, + serial: u32, + ) { if let Some(src) = &dnd.src { ipc::offer_source_to::(src, &surface.client); src.for_each_data_offer(|offer| { diff --git a/src/ifs/wl_seat/kb_owner.rs b/src/ifs/wl_seat/kb_owner.rs index b820d7d6..3e71bd4b 100644 --- a/src/ifs/wl_seat/kb_owner.rs +++ b/src/ifs/wl_seat/kb_owner.rs @@ -2,6 +2,7 @@ use { crate::{ifs::wl_seat::WlSeatGlobal, tree::Node, utils::clonecell::CloneCell}, std::rc::Rc, }; +use crate::xwayland::XWaylandEvent; pub struct KbOwnerHolder { default: Rc, @@ -58,6 +59,9 @@ impl KbOwner for DefaultKbOwner { return; } log::info!("unfocus {}", old.node_id()); + if old.node_is_xwayland_surface() && !node.node_is_xwayland_surface() { + seat.state.xwayland.queue.push(XWaylandEvent::ActivateRoot); + } old.node_unfocus(seat); if old.node_seat_state().unfocus(seat) { old.node_active_changed(false); diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 62beff78..6be3c4ce 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -426,7 +426,12 @@ impl PointerOwner for DndPointerOwner { target.node_dnd_leave(&self.dnd); target.node_seat_state().remove_dnd_target(seat); target = node; - target.node_dnd_enter(&self.dnd, x, y, seat.state.next_serial(target.node_client().as_deref())); + target.node_dnd_enter( + &self.dnd, + x, + y, + seat.state.next_serial(target.node_client().as_deref()), + ); target.node_seat_state().add_dnd_target(seat); self.target.set(target); } else if (self.pos_x.get(), self.pos_y.get()) != (x, y) { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index f7f9ef21..668a6a8d 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -866,6 +866,10 @@ impl SizedNode for WlSurface { fn dnd_motion(&self, dnd: &Dnd, x: Fixed, y: Fixed) { dnd.seat.dnd_surface_motion(self, dnd, x, y); } + + fn is_xwayland_surface(&self) -> bool { + self.client.is_xwayland + } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs index def34d6d..4fa40e7b 100644 --- a/src/ifs/wl_surface/xwindow.rs +++ b/src/ifs/wl_surface/xwindow.rs @@ -16,7 +16,7 @@ use { }, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode, - queue::AsyncQueue, smallmap::SmallMap, + smallmap::SmallMap, }, wire::WlSurfaceId, wire_xcon::CreateNotify, @@ -139,7 +139,6 @@ pub struct Xwindow { pub surface: Rc, pub parent_node: CloneCell>>, pub focus_history: SmallMap>, 1>, - pub events: Rc>, pub workspace: CloneCell>>, pub display_link: RefCell>>>, pub toplevel_data: ToplevelData, @@ -207,7 +206,6 @@ impl Xwindow { pub fn new( data: &Rc, surface: &Rc, - events: &Rc>, ) -> Self { Self { id: data.state.node_ids.next(), @@ -216,7 +214,6 @@ impl Xwindow { surface: surface.clone(), parent_node: Default::default(), focus_history: Default::default(), - events: events.clone(), workspace: Default::default(), display_link: Default::default(), toplevel_data: Default::default(), @@ -249,7 +246,11 @@ impl Xwindow { _ => return, }; let extents = self.surface.extents.get(); - // parent.child_active_changed(self, self.active_surfaces.get() > 0); + parent.clone().node_child_active_changed( + self, + self.toplevel_data.active_surfaces.get() > 0, + 1, + ); parent.node_child_size_changed(self, extents.width(), extents.height()); parent.node_child_title_changed( self, @@ -322,8 +323,7 @@ impl SurfaceExt for Xwindow { self.surface.unset_ext(); self.data.window.set(None); self.data.surface_id.set(None); - self.events - .push(XWaylandEvent::SurfaceDestroyed(self.surface.id)); + self.data.state.xwayland.queue.push(XWaylandEvent::SurfaceDestroyed(self.surface.id)); Ok(()) } @@ -389,7 +389,7 @@ impl SizedNode for Xwindow { } fn close(&self) { - self.events.push(XWaylandEvent::Close(self.data.clone())); + self.data.state.xwayland.queue.push(XWaylandEvent::Close(self.data.clone())); } fn absolute_position(&self) -> Rect { @@ -427,7 +427,7 @@ impl SizedNode for Xwindow { let old = self.data.info.extents.replace(*rect); if old != *rect { if !self.data.info.override_redirect.get() { - self.events.push(XWaylandEvent::Configure(self.clone())); + self.data.state.xwayland.queue.push(XWaylandEvent::Configure(self.clone())); } if old.position() != rect.position() { self.surface.set_absolute_position(rect.x1(), rect.y1()); @@ -482,7 +482,7 @@ impl ToplevelNode for Xwindow { } fn activate(&self) { - self.events.push(XWaylandEvent::Activate(self.data.clone())); + self.data.state.xwayland.queue.push(XWaylandEvent::Activate(self.data.clone())); } fn toggle_floating(self: Rc) { @@ -503,7 +503,7 @@ impl ToplevelNode for Xwindow { } fn close(&self) { - self.events.push(XWaylandEvent::Close(self.data.clone())); + self.data.state.xwayland.queue.push(XWaylandEvent::Close(self.data.clone())); } } diff --git a/src/state.rs b/src/state.rs index 079d35d7..f80e9fc3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -44,6 +44,7 @@ use { time::Duration, }, }; +use crate::xwayland::XWaylandEvent; pub struct State { pub xkb_ctx: XkbContext, @@ -92,6 +93,7 @@ pub struct State { pub struct XWaylandState { pub enabled: Cell, pub handler: RefCell>>, + pub queue: Rc>, } pub struct IdleState { @@ -101,6 +103,14 @@ pub struct IdleState { pub timeout_changed: Cell, } +impl IdleState { + pub fn set_timeout(&self, timeout: Duration) { + self.timeout.set(timeout); + self.timeout_changed.set(true); + self.change.trigger(); + } +} + pub struct InputDeviceData { pub handler: SpawnedFuture<()>, pub id: InputDeviceId, diff --git a/src/tree.rs b/src/tree.rs index 65eb99dd..bd424ca2 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -359,6 +359,10 @@ pub trait SizedNode: Sized + 'static { let _ = x; let _ = y; } + + fn is_xwayland_surface(&self) -> bool { + false + } } pub trait Node { @@ -436,6 +440,7 @@ pub trait Node { fn node_set_parent(self: Rc, parent: Rc); fn node_client(&self) -> Option>; fn node_client_id(&self) -> Option; + fn node_is_xwayland_surface(&self) -> bool; fn node_into_surface(self: Rc) -> Option>; fn node_dnd_drop(&self, dnd: &Dnd); fn node_dnd_leave(&self, dnd: &Dnd); @@ -644,6 +649,10 @@ impl Node for T { fn node_client_id(&self) -> Option { ::client_id(self) } + fn node_is_xwayland_surface(&self) -> bool { + ::is_xwayland_surface(self) + } + fn node_into_surface(self: Rc) -> Option> { ::into_surface(&self) } diff --git a/src/xwayland.rs b/src/xwayland.rs index f4669ea3..e50f03e3 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -13,7 +13,7 @@ use { }, state::State, user_session::import_environment, - utils::{errorfmt::ErrorFmt, oserror::OsError, queue::AsyncQueue, tri::Try}, + utils::{errorfmt::ErrorFmt, oserror::OsError, tri::Try}, wire::WlSurfaceId, xcon::XconError, xwayland::{xsocket::allocate_socket, xwm::Wm}, @@ -168,7 +168,6 @@ async fn run( Err(e) => return Err(XWaylandError::ExecFailed(e)), }; let client_id = state.clients.id(); - let queue = Rc::new(AsyncQueue::new()); let client = state.clients.spawn2( client_id, state, @@ -176,21 +175,22 @@ async fn run( 9999, 9999, true, - Some(queue.clone()), + true, ); let client = match client { Ok(c) => c, Err(e) => return Err(XWaylandError::SpawnClient(e)), }; state.eng.fd(&Rc::new(dfdread))?.readable().await?; - let wm = match Wm::get(state, client, wm1, queue.clone()).await { + state.xwayland.queue.clear(); + let wm = match Wm::get(state, client, wm1).await { Ok(w) => w, Err(e) => return Err(XWaylandError::CreateWm(Box::new(e))), }; let wm = state.eng.spawn(wm.run()); state.eng.fd(&Rc::new(pidfd))?.readable().await?; drop(wm); - queue.clear(); + state.xwayland.queue.clear(); stderr_read.await; Ok(()) } @@ -278,5 +278,6 @@ pub enum XWaylandEvent { SurfaceDestroyed(WlSurfaceId), Configure(Rc), Activate(Rc), + ActivateRoot, Close(Rc), } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 6540708c..2cf2b5f3 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -9,7 +9,7 @@ use { state::State, tree::{Node, SizedNode}, utils::{ - bitflags::BitflagsExt, errorfmt::ErrorFmt, linkedlist::LinkedList, queue::AsyncQueue, + bitflags::BitflagsExt, errorfmt::ErrorFmt, linkedlist::LinkedList, }, wire::WlSurfaceId, wire_xcon::{ @@ -136,7 +136,6 @@ pub struct Wm { client: Rc, windows: AHashMap>, windows_by_surface_id: AHashMap>, - queue: Rc>, focus_window: Option>, last_input_serial: u64, @@ -162,7 +161,6 @@ impl Wm { state: &Rc, client: Rc, socket: OwnedFd, - queue: Rc>, ) -> Result { let c = match Xcon::connect_to_fd(&state.eng, &Rc::new(socket), &[], &[]).await { Ok(c) => c, @@ -344,7 +342,6 @@ impl Wm { client, windows: Default::default(), windows_by_surface_id: Default::default(), - queue, focus_window: Default::default(), last_input_serial: 0, stack_list: Default::default(), @@ -357,7 +354,7 @@ impl Wm { pub async fn run(mut self) { loop { select! { - e = self.queue.pop().fuse() => self.handle_xwayland_event(e).await, + e = self.state.xwayland.queue.pop().fuse() => self.handle_xwayland_event(e).await, e = self.c.event().fuse() => self.handle_event(&e).await, } } @@ -371,6 +368,7 @@ impl Wm { XWaylandEvent::Configure(event) => self.handle_xwayland_configure(event).await, XWaylandEvent::SurfaceDestroyed(event) => self.handle_xwayland_surface_destroyed(event), XWaylandEvent::Activate(window) => self.activate_window(Some(&window)).await, + XWaylandEvent::ActivateRoot => self.activate_window(None).await, XWaylandEvent::Close(window) => self.close_window(&window).await, } } @@ -896,7 +894,7 @@ impl Wm { log::error!("The xwindow has already been constructed"); return; } - let window = Rc::new(Xwindow::new(data, &surface, &self.queue)); + let window = Rc::new(Xwindow::new(data, &surface)); if let Err(e) = window.install() { log::error!( "Could not attach the xwindow to the surface: {}", diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index 81e95fb9..90095c41 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -18,3 +18,7 @@ msg set_log_level = 3 { msg take_screenshot = 4 { id: id(jay_screenshot), } + +msg get_idle = 5 { + id: id(jay_idle), +} diff --git a/wire/jay_idle.txt b/wire/jay_idle.txt new file mode 100644 index 00000000..3a0098ec --- /dev/null +++ b/wire/jay_idle.txt @@ -0,0 +1,18 @@ +# requests + +msg get_status = 0 { +} + +msg set_interval = 1 { + interval: pod(u64), +} + +# events + +msg interval = 0 { + interval: pod(u64), +} + +msg inhibitor = 1 { + name: str, +}