diff --git a/build/wire_xcon.rs b/build/wire_xcon.rs index 7ec5ce8a..bb566c94 100644 --- a/build/wire_xcon.rs +++ b/build/wire_xcon.rs @@ -141,7 +141,12 @@ impl<'a> Parser<'a> { let (_, body) = self.expect_tree(TreeDelim::Brace)?; let mut parser = Parser::new(body); let data = parser.parse_struct_body(name)?; - Ok(Event { opcode, xge, data }) + Ok(Event { + opcode, + xge, + data, + ext_idx: self.ext_idx, + }) })(); res.with_context(|| format!("While parsing event starting at line {}", line)) } @@ -779,6 +784,7 @@ struct Event { opcode: u32, xge: bool, data: Struct, + ext_idx: Option, } #[derive(Debug)] @@ -941,6 +947,7 @@ enum StructUsecase<'a> { EventCopy { copy: &'a EventCopy, original: &'a Struct, + xge: bool, }, } @@ -950,6 +957,7 @@ fn format_xevent( s: &Struct, opcode: u32, protocols: &Protocols, + ext: Option, ) -> Result<()> { let lt_a = if struct_needs_lt(s, protocols)? { "<'a>" @@ -958,6 +966,7 @@ fn format_xevent( }; writeln!(f)?; writeln!(f, "impl<'a> XEvent<'a> for {}{lt_a} {{", name)?; + writeln!(f, " const EXTENSION: Option = {:?};", ext)?; writeln!(f, " const OPCODE: u16 = {:?};", opcode)?; writeln!(f, "}}")?; Ok(()) @@ -965,7 +974,14 @@ fn format_xevent( fn format_event(f: &mut F, s: &Event, protocols: &Protocols) -> Result<()> { format_struct(f, &s.data, protocols, &StructUsecase::Event { xge: s.xge })?; - format_xevent(f, s.data.name.as_bstr(), &s.data, s.opcode, protocols) + format_xevent( + f, + s.data.name.as_bstr(), + &s.data, + s.opcode, + protocols, + s.ext_idx, + ) } fn format_eventcopy(f: &mut F, s: &EventCopy, protocols: &Protocols) -> Result<()> { @@ -980,9 +996,17 @@ fn format_eventcopy(f: &mut F, s: &EventCopy, protocols: &Protocols) - &StructUsecase::EventCopy { copy: s, original: &original.data, + xge: original.xge, }, )?; - format_xevent(f, s.name.as_bstr(), &original.data, s.opcode, protocols) + format_xevent( + f, + s.name.as_bstr(), + &original.data, + s.opcode, + protocols, + original.ext_idx, + ) } fn format_request(f: &mut F, s: &Request, protocols: &Protocols) -> Result<()> { @@ -1276,7 +1300,7 @@ fn format_struct( writeln!(f, "#[derive(Debug, Clone)]")?; writeln!(f, "pub struct {}{lt_a} {{", struct_name)?; if let StructUsecase::EventCopy { original, .. } = usecase { - writeln!(f, " data: {}{lt_a},", original.name)?; + writeln!(f, " pub data: {}{lt_a},", original.name)?; } else { for field in &s.fields { if let Field::Real(rf) = field { @@ -1308,87 +1332,97 @@ fn format_struct( writeln!(f, " type Generic<'b> = {}{lt_b};", struct_name)?; writeln!(f, " const IS_POD: bool = false;")?; writeln!(f, " const HAS_FDS: bool = {has_fds};")?; - if !matches!( + let mut write_serialize = true; + if matches!( usecase, - StructUsecase::Event { .. } | StructUsecase::EventCopy { .. } | StructUsecase::Reply + StructUsecase::Reply + | StructUsecase::Event { xge: true } + | StructUsecase::EventCopy { xge: true, .. } ) { + write_serialize = false; + } + if write_serialize { writeln!(f)?; writeln!(f, " fn serialize(&self, formatter: &mut Formatter) {{")?; - for group in &groups { - match group { - FieldGroup::Pods { fields, .. } => { - writeln!(f, " {{")?; - for field in fields { - if let Field::Real(rf) = field { - write!(f, " let {}_bytes = ", rf.name)?; - match &rf.value { - Some(e) => { - writeln!(f, "{{")?; - write!(f, " let tmp: ")?; - write_type(f, &rf.ty, protocols)?; - write!(f, " = (")?; - write_expr(f, e, "self.")?; - writeln!(f, ") as _;")?; - writeln!(f, " tmp.to_ne_bytes()")?; - writeln!(f, " }};")?; + if let StructUsecase::EventCopy { .. } = usecase { + writeln!(f, " self.data.serialize(formatter);")?; + } else { + for group in &groups { + match group { + FieldGroup::Pods { fields, .. } => { + writeln!(f, " {{")?; + for field in fields { + if let Field::Real(rf) = field { + write!(f, " let {}_bytes = ", rf.name)?; + match &rf.value { + Some(e) => { + writeln!(f, "{{")?; + write!(f, " let tmp: ")?; + write_type(f, &rf.ty, protocols)?; + write!(f, " = (")?; + write_expr(f, e, "self.")?; + writeln!(f, ") as _;")?; + writeln!(f, " tmp.to_ne_bytes()")?; + writeln!(f, " }};")?; + } + _ => writeln!(f, "self.{}.to_ne_bytes();", rf.name)?, } - _ => writeln!(f, "self.{}.to_ne_bytes();", rf.name)?, } } + writeln!(f, " formatter.write_bytes(&[")?; + for field in fields { + match field { + Field::Pad(n) => { + write!(f, " ")?; + for _ in 0..*n { + write!(f, " 0,")?; + } + writeln!(f)?; + } + Field::Real(rf) => { + let num_bytes = match rf.ty { + Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I32 | Type::U32 => 4, + Type::I64 | Type::U64 => 8, + _ => unreachable!(), + }; + write!(f, " ")?; + for i in 0..num_bytes { + write!(f, " {}_bytes[{}],", rf.name, i)?; + } + writeln!(f)?; + } + Field::Opcode(n) => { + writeln!(f, " {},", n)?; + } + Field::ExtMajor => { + writeln!(f, " formatter.ext_opcode(),")?; + } + _ => unreachable!(), + } + } + writeln!(f, " ]);")?; + writeln!(f, " }}")?; } - writeln!(f, " formatter.write_bytes(&[")?; - for field in fields { - match field { - Field::Pad(n) => { - write!(f, " ")?; - for _ in 0..*n { - write!(f, " 0,")?; - } - writeln!(f)?; + FieldGroup::Single(field) => match field { + Field::Align(n) => writeln!(f, " formatter.align({n});")?, + Field::Real(rf) => match &rf.value { + Some(v) => { + writeln!(f, " {{")?; + write!(f, " let tmp: ")?; + write_type(f, &rf.ty, protocols)?; + write!(f, " = ")?; + write_expr(f, v, "self.")?; + writeln!(f, " as _;")?; + writeln!(f, " tmp.serialize(formatter);")?; + writeln!(f, " }}")?; } - Field::Real(rf) => { - let num_bytes = match rf.ty { - Type::I8 | Type::U8 => 1, - Type::I16 | Type::U16 => 2, - Type::I32 | Type::U32 => 4, - Type::I64 | Type::U64 => 8, - _ => unreachable!(), - }; - write!(f, " ")?; - for i in 0..num_bytes { - write!(f, " {}_bytes[{}],", rf.name, i)?; - } - writeln!(f)?; - } - Field::Opcode(n) => { - writeln!(f, " {},", n)?; - } - Field::ExtMajor => { - writeln!(f, " formatter.ext_opcode(),")?; - } - _ => unreachable!(), - } - } - writeln!(f, " ]);")?; - writeln!(f, " }}")?; - } - FieldGroup::Single(field) => match field { - Field::Align(n) => writeln!(f, " formatter.align({n});")?, - Field::Real(rf) => match &rf.value { - Some(v) => { - writeln!(f, " {{")?; - write!(f, " let tmp: ")?; - write_type(f, &rf.ty, protocols)?; - write!(f, " = ")?; - write_expr(f, v, "self.")?; - writeln!(f, " as _;")?; - writeln!(f, " tmp.serialize(formatter);")?; - writeln!(f, " }}")?; - } - _ => writeln!(f, " self.{}.serialize(formatter);", rf.name)?, + _ => writeln!(f, " self.{}.serialize(formatter);", rf.name)?, + }, + _ => unreachable!(), }, - _ => unreachable!(), - }, + } } } writeln!(f, " }}")?; diff --git a/src/async_engine.rs b/src/async_engine.rs index 964f2523..efa9f71e 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -128,7 +128,7 @@ mod yield_ { if self.queue.iteration() > self.iteration { Poll::Ready(()) } else { - cx.waker().wake_by_ref(); + self.queue.push_yield(cx.waker().clone()); Poll::Pending } } @@ -435,16 +435,17 @@ mod queue { use crate::event_loop::{EventLoop, EventLoopDispatcher, EventLoopId}; use crate::utils::array; use crate::utils::numcell::NumCell; + use crate::utils::syncqueue::SyncQueue; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; use std::error::Error; - use std::mem; - use std::ops::DerefMut; use std::rc::Rc; + use std::task::Waker; pub(super) struct Dispatcher { queue: Rc, stash: RefCell>, + yield_stash: RefCell>, } impl Dispatcher { @@ -457,10 +458,12 @@ mod queue { num_queued: Default::default(), queues: array::from_fn(|_| Default::default()), iteration: Default::default(), + yields: Default::default(), }); let slf = Rc::new(Dispatcher { queue: queue.clone(), - stash: RefCell::new(Default::default()), + stash: Default::default(), + yield_stash: Default::default(), }); el.insert(id, None, 0, slf)?; Ok(queue) @@ -470,11 +473,12 @@ mod queue { impl EventLoopDispatcher for Dispatcher { fn dispatch(self: Rc, _events: i32) -> Result<(), Box> { let mut stash = self.stash.borrow_mut(); + let mut yield_stash = self.yield_stash.borrow_mut(); while self.queue.num_queued.get() > 0 { self.queue.iteration.fetch_add(1); let mut phase = 0; while phase < NUM_PHASES as usize { - mem::swap(&mut *stash, &mut *self.queue.queues[phase].borrow_mut()); + self.queue.queues[phase].swap(&mut *stash); if stash.is_empty() { phase += 1; continue; @@ -484,6 +488,10 @@ mod queue { runnable.run(); } } + self.queue.yields.swap(&mut *yield_stash); + for waker in yield_stash.drain(..) { + waker.wake(); + } } self.queue.dispatch_scheduled.set(false); Ok(()) @@ -494,7 +502,7 @@ mod queue { fn drop(&mut self) { let _ = self.queue.el.remove(self.queue.id); for queue in &self.queue.queues { - mem::take(queue.borrow_mut().deref_mut()); + queue.swap(&mut VecDeque::new()); } } } @@ -504,13 +512,14 @@ mod queue { id: EventLoopId, el: Rc, num_queued: NumCell, - queues: [RefCell>; NUM_PHASES], + queues: [SyncQueue; NUM_PHASES], iteration: NumCell, + yields: SyncQueue, } impl DispatchQueue { pub fn push(&self, runnable: Runnable, phase: Phase) { - self.queues[phase as usize].borrow_mut().push_back(runnable); + self.queues[phase as usize].push(runnable); self.num_queued.fetch_add(1); if !self.dispatch_scheduled.get() { let _ = self.el.schedule(self.id); @@ -518,6 +527,10 @@ mod queue { } } + pub fn push_yield(&self, waker: Waker) { + self.yields.push(waker); + } + pub fn iteration(&self) -> u64 { self.iteration.get() } diff --git a/src/client.rs b/src/client.rs index 55301c0b..4c0e7e7b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -211,8 +211,6 @@ pub struct Client { pub xwayland_queue: Option>>, } -const MAX_PENDING_BUFFERS: usize = 10; - impl Client { pub fn invalid_request(&self, obj: &dyn Object, request: u32) { log::error!( @@ -303,7 +301,7 @@ impl Client { fmt.write_len(); if swapchain.cur.is_full() { swapchain.commit(); - if swapchain.pending.len() > MAX_PENDING_BUFFERS { + if swapchain.exceeds_limit() { if !self.checking_queue_size.replace(true) { self.state.slow_clients.push(self.clone()); } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index fbdf07fd..ef85e109 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -66,10 +66,6 @@ impl NodeSeatState { self.dnd_targets.remove(&seat.id); } - // pub fn remove_pointer_grabs(&self) { - // self.grabs.clear(); - // } - pub fn is_active(&self) -> bool { self.kb_foci.len() > 0 } @@ -85,7 +81,7 @@ impl NodeSeatState { seat.ungrab_kb(); seat.keyboard_node.set(seat.state.root.clone()); if let Some(tl) = seat.toplevel_focus_history.last() { - seat.focus_node(tl.focus_surface(&seat)); + seat.focus_node(tl.focus_surface(seat.id)); } } } @@ -198,7 +194,11 @@ impl WlSeatGlobal { pub fn last_tiled_keyboard_toplevel(&self, new: &dyn Node) -> Option> { let is_container = new.is_container(); for tl in self.toplevel_focus_history.rev_iter() { - if !tl.parent_is_float() && (!is_container || !tl.is_contained_in(new.id())) { + let parent_is_float = match tl.parent() { + Some(pn) => pn.is_float(), + _ => false, + }; + if !parent_is_float && (!is_container || !tl.as_node().is_contained_in(new.id())) { return Some(tl.deref().clone()); } } @@ -214,8 +214,8 @@ impl WlSeatGlobal { pub fn focus_toplevel(self: &Rc, n: Rc) { let node = self.toplevel_focus_history.add_last(n.clone()); - n.set_focus_history_link(self, node); - self.focus_node(n.focus_surface(self)); + n.data().toplevel_history.insert(self.id, node); + self.focus_node(n.focus_surface(self.id)); } fn ungrab_kb(self: &Rc) { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 4ae57942..467b6af7 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -12,7 +12,7 @@ use crate::ifs::wl_callback::WlCallback; use crate::ifs::wl_seat::{Dnd, NodeSeatState, SeatId, WlSeatGlobal}; use crate::ifs::wl_surface::cursor::CursorSurface; use crate::ifs::wl_surface::wl_subsurface::WlSubsurface; -use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceRole}; +use crate::ifs::wl_surface::xdg_surface::{XdgSurfaceError}; use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error; use crate::leaks::Tracker; use crate::object::Object; @@ -20,7 +20,7 @@ use crate::pixman::Region; use crate::rect::Rect; use crate::render::Renderer; use crate::tree::walker::NodeVisitor; -use crate::tree::{ContainerSplit, Node, NodeId}; +use crate::tree::{ContainerNode, ContainerSplit, Node, NodeId}; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::utils::clonecell::CloneCell; use crate::utils::linkedlist::LinkedList; @@ -37,6 +37,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::rc::Rc; use thiserror::Error; +use crate::tree::toplevel::ToplevelNode; #[allow(dead_code)] const INVALID_SCALE: u32 = 0; @@ -88,7 +89,7 @@ pub struct WlSurface { ext: CloneCell>, pub frame_requests: RefCell>>, seat_state: NodeSeatState, - xdg: CloneCell>>, + toplevel: CloneCell>>, cursors: SmallMap, 1>, pub dnd_icons: SmallMap, 1>, pub tracker: Tracker, @@ -203,7 +204,7 @@ impl WlSurface { ext: CloneCell::new(client.state.none_surface_ext.clone()), frame_requests: RefCell::new(vec![]), seat_state: Default::default(), - xdg: Default::default(), + toplevel: Default::default(), cursors: Default::default(), dnd_icons: Default::default(), tracker: Default::default(), @@ -242,10 +243,10 @@ impl WlSurface { } pub fn accepts_kb_focus(&self) -> bool { - if let Some(xdg) = self.xdg.get() { - return xdg.role() == XdgSurfaceRole::XdgToplevel; + match self.toplevel.get() { + Some(tl) => tl.accepts_keyboard_focus(), + _ => self.ext.get().accepts_kb_focus(), } - self.ext.get().accepts_kb_focus() } fn send_enter(&self, output: WlOutputId) { @@ -255,19 +256,19 @@ impl WlSurface { }) } - fn set_xdg_surface(&self, xdg: Option>) { + fn set_toplevel(&self, tl: Option>) { let ch = self.children.borrow(); if let Some(ch) = &*ch { for ss in ch.subsurfaces.values() { - ss.surface.set_xdg_surface(xdg.clone()); + ss.surface.set_toplevel(tl.clone()); } } if self.seat_state.is_active() { - if let Some(xdg) = &xdg { - xdg.surface_active_changed(true); + if let Some(tl) = &tl { + tl.surface_active_changed(true); } } - self.xdg.set(xdg); + self.toplevel.set(tl); } pub fn set_role(&self, role: SurfaceRole) -> Result<(), WlSurfaceError> { @@ -367,7 +368,7 @@ impl WlSurface { } self.buffer.set(None); self.frame_requests.borrow_mut().clear(); - self.xdg.set(None); + self.toplevel.set(None); self.client.remove_obj(self)?; Ok(()) } @@ -588,7 +589,7 @@ impl Object for WlSurface { self.unset_ext(); mem::take(self.frame_requests.borrow_mut().deref_mut()); self.buffer.set(None); - self.xdg.set(None); + self.toplevel.set(None); } } @@ -611,18 +612,19 @@ impl Node for WlSurface { ss.surface.destroy_node(false); } } - if let Some(xdg) = self.xdg.get() { + if let Some(tl) = self.toplevel.get() { + let data = tl.data(); let mut remove = vec![]; - for (seat, s) in &xdg.focus_surface { + for (seat, s) in data.focus_surface.iter() { if s.id == self.id { remove.push(seat); } } for seat in remove { - xdg.focus_surface.remove(&seat); + data.focus_surface.remove(&seat); } if self.seat_state.is_active() { - xdg.surface_active_changed(false); + tl.surface_active_changed(false); } } self.seat_state.destroy_node(self); @@ -642,39 +644,71 @@ impl Node for WlSurface { } fn get_parent_mono(&self) -> Option { - self.xdg.get().and_then(|x| x.get_mono()) + self.toplevel.get() + .and_then(|t| t.parent()) + .and_then(|p| p.get_mono()) } fn get_parent_split(&self) -> Option { - self.xdg.get().and_then(|x| x.get_split()) + self.toplevel.get() + .and_then(|t| t.parent()) + .and_then(|p| p.get_split()) } fn set_parent_mono(&self, mono: bool) { - self.xdg.get().map(|x| x.set_mono(mono)); + if let Some(tl) = self.toplevel.get() { + if let Some(pn) = tl.parent() { + let node = if mono { Some(tl.as_node()) } else { None }; + pn.set_mono(node) + } + } } fn set_parent_split(&self, split: ContainerSplit) { - self.xdg.get().map(|x| x.set_split(split)); + if let Some(tl) = self.toplevel.get() { + if let Some(pn) = tl.parent() { + pn.set_split(split); + } + } } fn create_split(self: Rc, split: ContainerSplit) { - self.xdg.get().map(|x| x.create_split(split)); + let tl = match self.toplevel.get() { + Some(tl) => tl, + _ => return, + }; + let ws = match tl.workspace() { + Some(ws) => ws, + _ => return, + }; + let pn = match tl.parent() { + Some(pn) => pn, + _ => return, + }; + let cn = ContainerNode::new( + &self.client.state, + &ws, + pn.clone(), + tl.clone().into_node(), + split, + ); + pn.replace_child(tl.as_node(), cn); } fn move_focus(self: Rc, seat: &Rc, direction: Direction) { - let xdg = match self.xdg.get() { - Some(x) => x, - _ => return, - }; - xdg.move_focus(seat, direction); + if let Some(tl) = self.toplevel.get() { + if let Some(pn) = tl.parent() { + pn.move_focus_from_child(seat, tl.as_node(), direction); + } + } } fn move_self(self: Rc, direction: Direction) { - let xdg = match self.xdg.get() { - Some(x) => x, - _ => return, - }; - xdg.move_self(direction); + if let Some(tl) = self.toplevel.get() { + if let Some(pn) = tl.parent() { + pn.move_child(tl.into_node(), direction); + } + } } fn absolute_position(&self) -> Rect { @@ -682,8 +716,8 @@ impl Node for WlSurface { } fn active_changed(&self, active: bool) { - if let Some(xdg) = self.xdg.get() { - xdg.surface_active_changed(active); + if let Some(tl) = self.toplevel.get() { + tl.surface_active_changed(active); } } @@ -704,21 +738,22 @@ impl Node for WlSurface { } fn focus(self: Rc, seat: &Rc) { - if let Some(xdg) = self.xdg.get() { - xdg.focus_surface.insert(seat.id(), self.clone()); + if let Some(tl) = self.toplevel.get() { + tl.data().focus_surface.insert(seat.id(), self.clone()); + tl.activate(); } seat.focus_surface(&self); } fn focus_parent(&self, seat: &Rc) { - if let Some(xdg) = self.xdg.get() { - xdg.focus_parent(seat); + if let Some(tl) = self.toplevel.get() { + tl.parent().map(|p| p.focus_self(seat)); } } - fn toggle_floating(self: Rc, seat: &Rc) { - if let Some(xdg) = self.xdg.get() { - xdg.toggle_floating(seat); + fn toggle_floating(self: Rc, _seat: &Rc) { + if let Some(tl) = self.toplevel.get() { + tl.toggle_floating(); } } diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index cadc5de3..4800688c 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -2,7 +2,7 @@ pub mod xdg_popup; pub mod xdg_toplevel; use crate::client::ClientError; -use crate::ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}; +use crate::ifs::wl_seat::{NodeSeatState}; use crate::ifs::wl_surface::xdg_surface::xdg_popup::{XdgPopup, XdgPopupError}; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; use crate::ifs::wl_surface::{ @@ -12,16 +12,14 @@ use crate::ifs::xdg_wm_base::XdgWmBase; use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; -use crate::tree::{ContainerSplit, FindTreeResult, FoundNode, Node, WorkspaceNode}; +use crate::tree::{FindTreeResult, FoundNode, Node, WorkspaceNode}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; -use crate::utils::smallmap::SmallMap; use crate::wire::xdg_surface::*; use crate::wire::{WlSurfaceId, XdgPopupId, XdgSurfaceId}; use crate::NumCell; -use jay_config::Direction; use std::cell::Cell; use std::fmt::Debug; use std::rc::Rc; @@ -63,7 +61,6 @@ pub struct XdgSurface { ext: CloneCell>>, popups: CopyHashMap>, pending: PendingXdgSurfaceData, - pub(super) focus_surface: SmallMap, 1>, seat_state: NodeSeatState, pub workspace: CloneCell>>, pub tracker: Tracker, @@ -75,43 +72,6 @@ struct PendingXdgSurfaceData { } pub trait XdgSurfaceExt: Debug { - fn focus_parent(&self, seat: &Rc) { - let _ = seat; - } - - fn toggle_floating(self: Rc, seat: &Rc) { - let _ = seat; - } - - fn get_mono(&self) -> Option { - None - } - - fn get_split(&self) -> Option { - None - } - - fn set_mono(&self, mono: bool) { - let _ = mono; - } - - fn set_split(&self, split: ContainerSplit) { - let _ = split; - } - - fn create_split(self: Rc, split: ContainerSplit) { - let _ = split; - } - - fn move_focus(self: Rc, seat: &Rc, direction: Direction) { - let _ = seat; - let _ = direction; - } - - fn move_self(self: Rc, direction: Direction) { - let _ = direction; - } - fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { Ok(()) } @@ -120,17 +80,9 @@ pub trait XdgSurfaceExt: Debug { // nothing } - fn into_node(self: Rc) -> Option> { - None - } - fn extents_changed(&self) { // nothing } - - fn surface_active_changed(self: Rc, active: bool) { - let _ = active; - } } impl XdgSurface { @@ -148,7 +100,6 @@ impl XdgSurface { ext: Default::default(), popups: Default::default(), pending: Default::default(), - focus_surface: Default::default(), seat_state: Default::default(), workspace: Default::default(), tracker: Default::default(), @@ -168,48 +119,6 @@ impl XdgSurface { } } - pub fn surface_active_changed(&self, active: bool) { - if let Some(ext) = self.ext.get() { - ext.surface_active_changed(active); - } - } - - pub fn get_mono(&self) -> Option { - self.ext.get().and_then(|e| e.get_mono()) - } - - pub fn get_split(&self) -> Option { - self.ext.get().and_then(|e| e.get_split()) - } - - pub fn set_mono(&self, mono: bool) { - self.ext.get().map(|e| e.set_mono(mono)); - } - - pub fn set_split(&self, split: ContainerSplit) { - self.ext.get().map(|e| e.set_split(split)); - } - - pub fn create_split(&self, split: ContainerSplit) { - self.ext.get().map(|e| e.create_split(split)); - } - - pub fn move_focus(&self, seat: &Rc, direction: Direction) { - if let Some(ext) = self.ext.get() { - ext.move_focus(seat, direction); - } - } - - pub fn move_self(&self, direction: Direction) { - if let Some(ext) = self.ext.get() { - ext.move_self(direction); - } - } - - pub fn role(&self) -> XdgSurfaceRole { - self.role.get() - } - fn set_workspace(&self, ws: &Rc) { self.workspace.set(Some(ws.clone())); let pu = self.popups.lock(); @@ -235,18 +144,6 @@ impl XdgSurface { Ok(()) } - pub fn focus_parent(&self, seat: &Rc) { - if let Some(ext) = self.ext.get() { - ext.focus_parent(seat); - } - } - - pub fn toggle_floating(&self, seat: &Rc) { - if let Some(ext) = self.ext.get() { - ext.toggle_floating(seat); - } - } - fn destroy_node(&self) { self.workspace.set(None); self.surface.destroy_node(false); @@ -278,7 +175,6 @@ impl XdgSurface { return Err(XdgSurfaceError::AlreadyAttached(self.surface.id)); } self.surface.ext.set(self.clone()); - self.surface.set_xdg_surface(Some(self.clone())); Ok(()) } @@ -293,8 +189,6 @@ impl XdgSurface { return Err(DestroyError::PopupsNotYetDestroyed); } } - self.focus_surface.clear(); - self.surface.set_xdg_surface(None); self.surface.unset_ext(); self.base.surfaces.remove(&self.id); self.surface.client.remove_obj(self)?; @@ -318,7 +212,8 @@ impl XdgSurface { let toplevel = Rc::new(XdgToplevel::new(req.id, self)); track!(self.surface.client, toplevel); self.surface.client.add_client_obj(&toplevel)?; - self.ext.set(Some(toplevel)); + self.ext.set(Some(toplevel.clone())); + self.surface.set_toplevel(Some(toplevel)); Ok(()) } @@ -422,7 +317,6 @@ impl Object for XdgSurface { } fn break_loops(&self) { - self.focus_surface.take(); self.ext.take(); self.popups.clear(); self.workspace.set(None); diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 5db7d2b3..6b9dd7f7 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -353,10 +353,6 @@ impl XdgSurfaceExt for XdgPopup { } } - fn into_node(self: Rc) -> Option> { - Some(self) - } - fn extents_changed(&self) { self.xdg.surface.client.state.tree_changed(); } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 708258cb..08f6ce5d 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -2,23 +2,21 @@ use crate::bugs::Bugs; use crate::client::{Client, ClientError}; use crate::cursor::KnownCursor; use crate::fixed::Fixed; -use crate::ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}; +use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}; use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::render::Renderer; use crate::tree::walker::NodeVisitor; -use crate::tree::{ContainerNode, ContainerSplit, FindTreeResult}; +use crate::tree::{FindTreeResult}; use crate::tree::{FoundNode, Node, NodeId, ToplevelNodeId, WorkspaceNode}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; use crate::utils::clonecell::CloneCell; -use crate::utils::linkedlist::LinkedNode; -use crate::utils::smallmap::SmallMap; use crate::wire::xdg_toplevel::*; use crate::wire::XdgToplevelId; -use crate::{bugs, NumCell}; +use crate::{bugs}; use ahash::{AHashMap, AHashSet}; use jay_config::Direction; use num_derive::FromPrimitive; @@ -27,9 +25,8 @@ use std::fmt::{Debug, Formatter}; use std::mem; use std::ops::Deref; use std::rc::Rc; - use crate::ifs::wl_surface::WlSurface; -use crate::tree::toplevel::ToplevelNode; +use crate::tree::toplevel::{ToplevelData, ToplevelNode}; use thiserror::Error; #[derive(Copy, Clone, Debug, FromPrimitive)] @@ -51,15 +48,10 @@ const STATE_MAXIMIZED: u32 = 1; const STATE_FULLSCREEN: u32 = 2; #[allow(dead_code)] const STATE_RESIZING: u32 = 3; -#[allow(dead_code)] const STATE_ACTIVATED: u32 = 4; -#[allow(dead_code)] const STATE_TILED_LEFT: u32 = 5; -#[allow(dead_code)] const STATE_TILED_RIGHT: u32 = 6; -#[allow(dead_code)] const STATE_TILED_TOP: u32 = 7; -#[allow(dead_code)] const STATE_TILED_BOTTOM: u32 = 8; #[derive(Copy, Clone, Eq, PartialEq, Debug)] @@ -77,8 +69,6 @@ pub struct XdgToplevel { pub parent: CloneCell>>, pub children: RefCell>>, states: RefCell>, - pub toplevel_history: SmallMap>, 1>, - active_surfaces: NumCell, pub decoration: Cell, bugs: Cell<&'static Bugs>, min_width: Cell>, @@ -87,6 +77,7 @@ pub struct XdgToplevel { max_height: Cell>, title: RefCell, pub tracker: Tracker, + toplevel_data: ToplevelData, } impl Debug for XdgToplevel { @@ -110,8 +101,6 @@ impl XdgToplevel { parent: Default::default(), children: RefCell::new(Default::default()), states: RefCell::new(states), - toplevel_history: Default::default(), - active_surfaces: Default::default(), decoration: Cell::new(Decoration::Server), bugs: Cell::new(&bugs::NONE), min_width: Cell::new(None), @@ -120,24 +109,7 @@ impl XdgToplevel { max_height: Cell::new(None), title: RefCell::new("".to_string()), tracker: Default::default(), - } - } - - pub fn set_active(self: &Rc, active: bool) { - if let Some(parent) = self.parent_node.get() { - parent.child_active_changed(&**self, active); - } - let changed = { - let mut states = self.states.borrow_mut(); - match active { - true => states.insert(STATE_ACTIVATED), - false => states.remove(&STATE_ACTIVATED), - } - }; - if changed { - let rect = self.xdg.absolute_desired_extents.get(); - self.send_configure_checked(rect.width(), rect.height()); - self.xdg.do_send_configure(); + toplevel_data: Default::default(), } } @@ -195,6 +167,7 @@ impl XdgToplevel { } } self.xdg.surface.client.remove_obj(self.deref())?; + self.xdg.surface.set_toplevel(None); Ok(()) } @@ -307,27 +280,15 @@ impl XdgToplevel { Ok(()) } - fn toggle_floating(self: &Rc) { - let parent = match self.parent_node.get() { - Some(p) => p, - _ => return, - }; - if parent.is_float() { - parent.remove_child(&**self); - self.map_tiled(); - } else if let Some(ws) = self.xdg.workspace.get() { - parent.remove_child(&**self); - self.map_floating(&ws); - } - } - fn notify_parent(&self) { let parent = match self.parent_node.get() { Some(p) => p, _ => return, }; let extents = self.xdg.extents.get(); - parent.clone().child_active_changed(self, self.active_surfaces.get() > 0); + parent + .clone() + .child_active_changed(self, self.toplevel_data.active_surfaces.get() > 0); parent.child_size_changed(self, extents.width(), extents.height()); parent.child_title_changed(self, self.title.borrow_mut().deref()); } @@ -400,7 +361,7 @@ impl Node for XdgToplevel { self.xdg.surface.client.state.tree_changed(); } } - self.toplevel_history.take(); + self.toplevel_data.clear(); self.xdg.destroy_node(); self.xdg.seat_state.destroy_node(self) } @@ -474,87 +435,72 @@ impl Node for XdgToplevel { } impl ToplevelNode for XdgToplevel { + fn data(&self) -> &ToplevelData { + &self.toplevel_data + } + fn parent(&self) -> Option> { self.parent_node.get() } - fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc { - self.xdg - .focus_surface - .get(&seat.id()) - .unwrap_or_else(|| self.xdg.surface.clone()) - } - - fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode>) { - self.toplevel_history.insert(seat.id(), link); + fn workspace(&self) -> Option> { + self.xdg.workspace.get() } fn as_node(&self) -> &dyn Node { self } + + fn into_node(self: Rc) -> Rc { + self + } + + fn accepts_keyboard_focus(&self) -> bool { + true + } + + fn default_surface(&self) -> Rc { + self.xdg.surface.clone() + } + + fn set_active(&self, active: bool) { + if let Some(parent) = self.parent_node.get() { + parent.child_active_changed(self, active); + } + let changed = { + let mut states = self.states.borrow_mut(); + match active { + true => states.insert(STATE_ACTIVATED), + false => states.remove(&STATE_ACTIVATED), + } + }; + if changed { + let rect = self.xdg.absolute_desired_extents.get(); + self.send_configure_checked(rect.width(), rect.height()); + self.xdg.do_send_configure(); + } + } + + fn activate(&self) { + // nothing + } + + fn toggle_floating(self: Rc) { + let parent = match self.parent_node.get() { + Some(p) => p, + _ => return, + }; + if parent.is_float() { + parent.remove_child(&*self); + self.map_tiled(); + } else if let Some(ws) = self.xdg.workspace.get() { + parent.remove_child(&*self); + self.map_floating(&ws); + } + } } impl XdgSurfaceExt for XdgToplevel { - fn focus_parent(&self, seat: &Rc) { - self.parent_node.get().map(|p| p.focus_self(seat)); - } - - fn toggle_floating(self: Rc, _seat: &Rc) { - Self::toggle_floating(&self); - } - - fn get_mono(&self) -> Option { - self.parent_node.get().and_then(|p| p.get_mono()) - } - - fn get_split(&self) -> Option { - self.parent_node.get().and_then(|p| p.get_split()) - } - - fn set_mono(&self, mono: bool) { - let node = if mono { - Some(self as &dyn Node) - } else { - None - }; - self.parent_node.get().map(move |p| p.set_mono(node)); - } - - fn set_split(&self, split: ContainerSplit) { - self.parent_node.get().map(|p| p.set_split(split)); - } - - fn create_split(self: Rc, split: ContainerSplit) { - let ws = match self.xdg.workspace.get() { - Some(ws) => ws, - _ => return, - }; - let pn = match self.parent_node.get() { - Some(pn) => pn, - _ => return, - }; - let cn = ContainerNode::new( - &self.xdg.surface.client.state, - &ws, - pn.clone(), - self.clone(), - split, - ); - pn.replace_child(&*self, cn); - } - - fn move_focus(self: Rc, seat: &Rc, direction: Direction) { - if let Some(pn) = self.parent_node.get() { - pn.move_focus_from_child(seat, &*self, direction); - } - } - - fn move_self(self: Rc, direction: Direction) { - if let Some(pn) = self.parent_node.get() { - pn.move_child(self, direction); - } - } - fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { self.send_configure(0, 0); Ok(()) @@ -600,28 +546,12 @@ impl XdgSurfaceExt for XdgToplevel { } } - fn into_node(self: Rc) -> Option> { - Some(self) - } - fn extents_changed(&self) { self.notify_parent(); if self.parent_node.get().is_some() { self.xdg.surface.client.state.tree_changed(); } } - - fn surface_active_changed(self: Rc, active: bool) { - if active { - if self.active_surfaces.fetch_add(1) == 0 { - self.set_active(true); - } - } else { - if self.active_surfaces.fetch_sub(1) == 1 { - self.set_active(false); - } - } - } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs index 1b57baa3..c82cadb0 100644 --- a/src/ifs/wl_surface/xwindow.rs +++ b/src/ifs/wl_surface/xwindow.rs @@ -5,28 +5,118 @@ use crate::ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}; use crate::ifs::wl_surface::{SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError}; use crate::rect::Rect; use crate::render::Renderer; -use crate::tree::toplevel::ToplevelNode; +use crate::tree::toplevel::{ToplevelData, ToplevelNode}; use crate::tree::walker::NodeVisitor; use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; +use crate::utils::copyhashmap::CopyHashMap; use crate::utils::linkedlist::LinkedNode; use crate::utils::smallmap::SmallMap; use crate::wire::WlSurfaceId; use crate::wire_xcon::CreateNotify; use crate::xwayland::XWaylandEvent; -use crate::{AsyncQueue, CloneCell, State}; +use crate::{AsyncQueue, CloneCell, State}; +use bstr::BString; use jay_config::Direction; use std::cell::{Cell, RefCell}; use std::rc::Rc; use thiserror::Error; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum XInputModel { + None, + Passive, + Local, + Global, +} + +impl Default for XInputModel { + fn default() -> Self { + Self::Passive + } +} + +#[derive(Default, Debug)] +pub struct IcccmHints { + pub flags: Cell, + pub input: Cell, + pub initial_state: Cell, + pub icon_pixmap: Cell, + pub icon_window: Cell, + pub icon_x: Cell, + pub icon_y: Cell, + pub icon_mask: Cell, + pub window_group: Cell, +} + +#[derive(Default, Debug)] +pub struct SizeHints { + pub flags: Cell, + pub x: Cell, + pub y: Cell, + pub width: Cell, + pub height: Cell, + pub min_width: Cell, + pub min_height: Cell, + pub max_width: Cell, + pub max_height: Cell, + pub width_inc: Cell, + pub height_inc: Cell, + pub min_aspect_num: Cell, + pub min_aspect_den: Cell, + pub max_aspect_num: Cell, + pub max_aspect_den: Cell, + pub base_width: Cell, + pub base_height: Cell, + pub win_gravity: Cell, +} + +#[derive(Default, Debug)] +pub struct MotifHints { + pub flags: Cell, + pub decorations: Cell, +} + +#[derive(Default, Debug)] +pub struct XwindowInfo { + pub has_alpha: Cell, + pub override_redirect: Cell, + pub extents: Cell, + pub instance: RefCell>, + pub class: RefCell>, + pub title: RefCell>, + pub role: RefCell>, + pub protocols: CopyHashMap, + pub window_types: CopyHashMap, + pub never_focus: Cell, + pub utf8_title: Cell, + pub icccm_hints: IcccmHints, + pub normal_hints: SizeHints, + pub motif_hints: MotifHints, + pub startup_id: RefCell>, + pub fullscreen: Cell, + pub modal: Cell, + pub maximized_vert: Cell, + pub maximized_horz: Cell, + pub minimized: Cell, + pub pid: Cell>, + pub input_model: Cell, + pub mapped: Cell, + pub wants_floating: Cell, +} + pub struct XwindowData { pub state: Rc, pub window_id: u32, - pub override_redirect: bool, - pub extents: Cell, pub client: Rc, pub surface_id: Cell>, pub window: CloneCell>>, + pub info: XwindowInfo, + pub children: CopyHashMap>, + pub parent: CloneCell>>, + pub stack_link: RefCell>>>, + pub map_link: Cell>>>, + pub startup_info: RefCell>, + pub destroyed: Cell, } tree_id!(XwindowId); @@ -35,12 +125,13 @@ pub struct Xwindow { pub seat_state: NodeSeatState, pub data: Rc, pub surface: Rc, - pub parent: CloneCell>>, + pub parent_node: CloneCell>>, pub focus_history: SmallMap>, 1>, pub events: Rc>, pub workspace: CloneCell>>, pub display_link: RefCell>>>, pub display_xlink: RefCell>>>, + pub toplevel_data: ToplevelData, } impl XwindowData { @@ -52,17 +143,43 @@ impl XwindowData { event.height as _, ) .unwrap(); - log::info!("extents = {:?}", extents); Self { state: state.clone(), window_id: event.window, - override_redirect: event.override_redirect != 0, - extents: Cell::new(extents), client: client.clone(), surface_id: Cell::new(None), window: Default::default(), + info: XwindowInfo { + override_redirect: Cell::new(event.override_redirect != 0), + extents: Cell::new(extents), + ..Default::default() + }, + children: Default::default(), + parent: Default::default(), + stack_link: Default::default(), + map_link: Default::default(), + startup_info: Default::default(), + destroyed: Cell::new(false), } } + + pub fn is_ancestor_of(&self, mut other: Rc) -> bool { + loop { + if other.window_id == self.window_id { + return true; + } + other = match other.parent.get() { + Some(p) => p, + _ => return false, + } + } + } +} + +pub enum Change { + None, + Map, + Unmap, } impl Xwindow { @@ -76,12 +193,13 @@ impl Xwindow { seat_state: Default::default(), data: data.clone(), surface: surface.clone(), - parent: Default::default(), + parent_node: Default::default(), focus_history: Default::default(), events: events.clone(), workspace: Default::default(), display_link: Default::default(), display_xlink: Default::default(), + toplevel_data: Default::default(), } } @@ -92,6 +210,7 @@ impl Xwindow { pub fn break_loops(&self) { self.destroy_node(true); + self.surface.set_toplevel(None); } pub fn install(self: &Rc) -> Result<(), XWindowError> { @@ -100,11 +219,12 @@ impl Xwindow { return Err(XWindowError::AlreadyAttached); } self.surface.ext.set(self.clone()); + self.surface.set_toplevel(Some(self.clone())); Ok(()) } fn notify_parent(&self) { - let parent = match self.parent.get() { + let parent = match self.parent_node.get() { Some(p) => p, _ => return, }; @@ -115,45 +235,47 @@ impl Xwindow { // parent.child_title_changed(self, self.title.borrow_mut().deref()); } - fn managed_post_commit(self: &Rc) { - let parent = self.parent.get(); - if self.surface.buffer.get().is_some() { - if parent.is_none() { - self.data.state.map_tiled(self.clone()); - } - } else { - if parent.is_some() { - self.destroy_node(true); - } + pub fn is_mapped(&self) -> bool { + self.parent_node.get().is_some() || self.display_link.borrow_mut().is_some() + } + + pub fn may_be_mapped(&self) -> bool { + self.surface.buffer.get().is_some() && self.data.info.mapped.get() + } + + fn map_change(&self) -> Change { + match (self.may_be_mapped(), self.is_mapped()) { + (true, false) => Change::Map, + (false, true) => Change::Unmap, + _ => Change::None, } } - fn unmanaged_post_commit(self: &Rc) { - let mut dl = self.display_link.borrow_mut(); - let mut dxl = self.display_xlink.borrow_mut(); - if self.surface.buffer.get().is_some() { - if dl.is_none() { - *dl = Some(self.data.state.root.stacked.add_last(self.clone())); - *dxl = Some(self.data.state.root.xstacked.add_last(self.clone())); + pub fn map_status_changed(self: &Rc) { + match self.map_change() { + Change::None => {} + Change::Unmap => self.destroy_node(true), + Change::Map if self.data.info.override_redirect.get() => { + *self.display_link.borrow_mut() = Some(self.data.state.root.stacked.add_last(self.clone())); + *self.display_xlink.borrow_mut() = Some(self.data.state.root.xstacked.add_last(self.clone())); self.data.state.tree_changed(); } - } else { - if dl.is_some() { - drop(dl); - drop(dxl); - self.destroy_node(true); - } + Change::Map if self.data.info.wants_floating.get() => { + let ws = self.data.state.root.outputs.lock().iter().next().unwrap().1.workspace.get().unwrap(); + // todo + let ext = self.data.info.extents.get(); + self.data.state.map_floating(self.clone(), ext.width(), ext.height(), &ws); + }, + Change::Map => { + self.data.state.map_tiled(self.clone()) + }, } } } impl SurfaceExt for Xwindow { fn post_commit(self: Rc) { - if self.data.override_redirect { - self.unmanaged_post_commit(); - } else { - self.managed_post_commit(); - } + self.map_status_changed(); } fn on_surface_destroy(&self) -> Result<(), WlSurfaceError> { @@ -185,11 +307,12 @@ impl Node for Xwindow { self.display_link.borrow_mut().take(); self.workspace.take(); self.focus_history.clear(); - if let Some(parent) = self.parent.take() { + if let Some(parent) = self.parent_node.take() { parent.remove_child(self); } self.surface.destroy_node(false); self.seat_state.destroy_node(self); + self.toplevel_data.clear(); } fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { @@ -201,7 +324,7 @@ impl Node for Xwindow { } fn is_contained_in(&self, other: NodeId) -> bool { - if let Some(parent) = self.parent.get() { + if let Some(parent) = self.parent_node.get() { if parent.id() == other { return true; } @@ -215,7 +338,7 @@ impl Node for Xwindow { } fn absolute_position(&self) -> Rect { - self.data.extents.get() + self.data.info.extents.get() } fn find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { @@ -247,7 +370,7 @@ impl Node for Xwindow { fn change_extents(self: Rc, rect: &Rect) { let nw = rect.width(); let nh = rect.height(); - let de = self.data.extents.replace(*rect); + let de = self.data.info.extents.replace(*rect); if de.width() != nw || de.height() != nh { self.events.push(XWaylandEvent::Configure(self.clone())); } @@ -258,7 +381,7 @@ impl Node for Xwindow { } fn set_parent(self: Rc, parent: Rc) { - self.parent.set(Some(parent)); + self.parent_node.set(Some(parent)); self.notify_parent(); } @@ -268,21 +391,47 @@ impl Node for Xwindow { } impl ToplevelNode for Xwindow { + fn data(&self) -> &ToplevelData { + &self.toplevel_data + } + fn parent(&self) -> Option> { - self.parent.get() + self.parent_node.get() } - fn focus_surface(&self, _seat: &WlSeatGlobal) -> Rc { - self.surface.clone() - } - - fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode>) { - self.focus_history.insert(seat.id(), link); + fn workspace(&self) -> Option> { + self.workspace.get() } fn as_node(&self) -> &dyn Node { self } + + fn into_node(self: Rc) -> Rc { + self + } + + fn accepts_keyboard_focus(&self) -> bool { + self.data.info.icccm_hints.input.get() != 0 + } + + fn default_surface(&self) -> Rc { + self.surface.clone() + } + + fn set_active(&self, active: bool) { + if let Some(pn) = self.parent_node.get() { + pn.child_active_changed(self, active); + } + } + + fn activate(&self) { + self.events.push(XWaylandEvent::Activate(self.data.clone())); + } + + fn toggle_floating(self: Rc) { + todo!() + } } #[derive(Debug, Error)] diff --git a/src/macros.rs b/src/macros.rs index c1f56ae0..e0e947b2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -361,7 +361,7 @@ macro_rules! assert_align_eq { }}; } -macro_rules! atom_manager { +macro_rules! atoms { { $name:ident; $($field_name:ident,)* diff --git a/src/render.rs b/src/render.rs index 2b0a5a27..12391656 100644 --- a/src/render.rs +++ b/src/render.rs @@ -72,7 +72,6 @@ pub enum RenderError { #[error("EGL display does not support `EGL_EXT_image_dma_buf_import_modifiers`")] DmaBufImport, #[error("GLES driver does not support `GL_OES_EGL_image`")] - #[allow(dead_code)] OesEglImage, #[error("EGL display does not support `EGL_KHR_image_base`")] ImageBase, diff --git a/src/render/egl/display.rs b/src/render/egl/display.rs index 770a5dfb..a2faecfc 100644 --- a/src/render/egl/display.rs +++ b/src/render/egl/display.rs @@ -50,9 +50,9 @@ impl EglDisplay { ctx, }; ctx.ext = ctx.with_current(|| Ok(get_gl_ext()))?; - // if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) { - // return Err(GlesError::OesEglImage); - // } + if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) { + return Err(RenderError::OesEglImage); + } Ok(Rc::new(ctx)) } } diff --git a/src/render/renderer/framebuffer.rs b/src/render/renderer/framebuffer.rs index 37957b43..5ea8400e 100644 --- a/src/render/renderer/framebuffer.rs +++ b/src/render/renderer/framebuffer.rs @@ -5,7 +5,7 @@ use crate::render::gl::sys::{ }; use crate::render::renderer::context::RenderContext; use crate::render::renderer::renderer::Renderer; -use crate::render::sys::{glBlendFunc, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, glFlush}; +use crate::render::sys::{glBlendFunc, glFlush, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}; use crate::tree::Node; use crate::State; use std::fmt::{Debug, Formatter}; diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 00940098..9085e3e3 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -15,9 +15,7 @@ use crate::render::renderer::context::RenderContext; use crate::render::sys::{glDisable, glEnable, GL_BLEND}; use crate::render::Texture; use crate::theme::Color; -use crate::tree::{ - ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode, -}; +use crate::tree::{ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode}; use crate::State; use std::ops::Deref; use std::rc::Rc; @@ -69,7 +67,6 @@ impl Renderer<'_> { 2.0 * (y as f32 / self.fb.height as f32) - 1.0 } - fn fill_boxes(&self, boxes: &[Rect], color: &Color) { self.fill_boxes2(boxes, color, 0, 0); } diff --git a/src/tree/container.rs b/src/tree/container.rs index 17b35cef..89e43526 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -315,7 +315,10 @@ impl ContainerNode { fn perform_mono_layout(self: &Rc, child: &ContainerChild) { let mb = self.mono_body.get(); - child.node.clone().change_extents(&mb.move_(self.abs_x1.get(), self.abs_y1.get())); + child + .node + .clone() + .change_extents(&mb.move_(self.abs_x1.get(), self.abs_y1.get())); self.mono_content .set(child.content.get().at_point(mb.x1(), mb.y1())); } @@ -566,7 +569,8 @@ impl ContainerNode { let mut pos = 0; for (i, c) in self.children.iter().enumerate() { if i > 0 { - rd.border_rects.push(Rect::new_sized(pos - bw, 0, bw, th).unwrap()); + rd.border_rects + .push(Rect::new_sized(pos - bw, 0, bw, th).unwrap()); } let mut width = space_per_child; if rem > 0 { @@ -583,9 +587,9 @@ impl ContainerNode { let title = c.title.borrow_mut(); match text::render(&ctx, width, th, &font, &title, Color::GREY) { Ok(t) => rd.titles.push(ContainerTitle { - x: pos, - y: 0, - tex: t, + x: pos, + y: 0, + tex: t, }), Err(e) => { log::error!("Could not render title {}: {}", title, ErrorFmt(e)); @@ -594,7 +598,8 @@ impl ContainerNode { } pos += width + bw; } - rd.underline_rects.push(Rect::new_sized(0, th, cwidth, 1).unwrap()); + rd.underline_rects + .push(Rect::new_sized(0, th, cwidth, 1).unwrap()); } else { let split = self.split.get(); for (i, c) in self.children.iter().enumerate() { @@ -607,7 +612,8 @@ impl ContainerNode { }; rd.border_rects.push(rect); } - let rect = Rect::new_sized(body.x1(), body.y1() - th - 1, body.width(), th).unwrap(); + let rect = + Rect::new_sized(body.x1(), body.y1() - th - 1, body.width(), th).unwrap(); if c.active.get() { rd.active_title_rects.push(rect); } else { @@ -799,7 +805,9 @@ impl Node for ContainerNode { matches!(direction, Direction::Left | Direction::Right) } else { match self.split.get() { - ContainerSplit::Horizontal => matches!(direction, Direction::Left | Direction::Right), + ContainerSplit::Horizontal => { + matches!(direction, Direction::Left | Direction::Right) + } ContainerSplit::Vertical => matches!(direction, Direction::Up | Direction::Down), } }; @@ -831,8 +839,12 @@ impl Node for ContainerNode { if mc.is_some() { self.mono_child.set(Some(sibling.clone())); let body = self.mono_body.get(); - self.mono_content.set(sibling.content.get().at_point(body.x1(), body.y1())); - sibling.node.clone().change_extents(&body.move_(self.abs_x1.get(), self.abs_y1.get())); + self.mono_content + .set(sibling.content.get().at_point(body.x1(), body.y1())); + sibling + .node + .clone() + .change_extents(&body.move_(self.abs_x1.get(), self.abs_y1.get())); } sibling.node.clone().do_focus(seat, direction); } @@ -1167,7 +1179,10 @@ impl Node for ContainerNode { .child_size_changed(&*self, rect.width(), rect.height()); } else { if let Some(c) = self.mono_child.get() { - let body = self.mono_body.get().move_(self.abs_x1.get(), self.abs_y1.get()); + let body = self + .mono_body + .get() + .move_(self.abs_x1.get(), self.abs_y1.get()); c.node.clone().change_extents(&body); } else { for child in self.children.iter() { @@ -1187,7 +1202,9 @@ impl Node for ContainerNode { fn set_parent(self: Rc, parent: Rc) { self.parent.set(parent.clone()); - parent.clone().child_active_changed(&*self, self.active.get()); + parent + .clone() + .child_active_changed(&*self, self.active.get()); parent.child_size_changed(&*self, self.width.get(), self.height.get()); parent .clone() diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 5c557258..16f5a0e8 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -1,19 +1,55 @@ -use crate::ifs::wl_seat::WlSeatGlobal; +use crate::ifs::wl_seat::{SeatId}; use crate::ifs::wl_surface::WlSurface; -use crate::tree::Node; +use crate::tree::{Node, WorkspaceNode}; use crate::utils::linkedlist::LinkedNode; use std::rc::Rc; +use crate::NumCell; +use crate::utils::smallmap::SmallMap; -pub trait ToplevelNode: Node { +pub trait ToplevelNode { + fn data(&self) -> &ToplevelData; fn parent(&self) -> Option>; - fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc; - fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode>); + fn workspace(&self) -> Option>; fn as_node(&self) -> &dyn Node; + fn into_node(self: Rc) -> Rc; + fn accepts_keyboard_focus(&self) -> bool; + fn default_surface(&self) -> Rc; + fn set_active(&self, active: bool); + fn activate(&self); + fn toggle_floating(self: Rc); +} - fn parent_is_float(&self) -> bool { - if let Some(parent) = self.parent() { - return parent.is_float(); - } - false +#[derive(Default)] +pub struct ToplevelData { + pub active_surfaces: NumCell, + pub focus_surface: SmallMap, 1>, + pub toplevel_history: SmallMap>, 1>, +} + +impl ToplevelData { + pub fn clear(&self) { + self.focus_surface.clear(); + self.toplevel_history.clear(); + } +} + +impl<'a> dyn ToplevelNode + 'a { + pub fn surface_active_changed(&self, active: bool) { + if active { + if self.data().active_surfaces.fetch_add(1) == 0 { + self.set_active(true); + } + } else { + if self.data().active_surfaces.fetch_sub(1) == 1 { + self.set_active(false); + } + } + } + + pub fn focus_surface(&self, seat: SeatId) -> Rc { + self.data() + .focus_surface + .get(&seat) + .unwrap_or_else(|| self.default_surface()) } } diff --git a/src/utils/bitflags.rs b/src/utils/bitflags.rs index 39c80f50..1a368b12 100644 --- a/src/utils/bitflags.rs +++ b/src/utils/bitflags.rs @@ -1,5 +1,6 @@ pub trait BitflagsExt { fn contains(self, other: Self) -> bool; + fn not_contains(self, other: Self) -> bool; fn intersects(self, other: Self) -> bool; } @@ -10,6 +11,10 @@ macro_rules! num { self & other == other } + fn not_contains(self, other: Self) -> bool { + self & other != other + } + fn intersects(self, other: Self) -> bool { self & other != 0 } diff --git a/src/utils/copyhashmap.rs b/src/utils/copyhashmap.rs index 05eae02c..aea91442 100644 --- a/src/utils/copyhashmap.rs +++ b/src/utils/copyhashmap.rs @@ -1,5 +1,6 @@ use ahash::AHashMap; use std::cell::{RefCell, RefMut}; +use std::fmt::{Debug, Formatter}; use std::hash::Hash; use std::mem; @@ -7,6 +8,12 @@ pub struct CopyHashMap { map: RefCell>, } +impl Debug for CopyHashMap { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.map.fmt(f) + } +} + impl Default for CopyHashMap { fn default() -> Self { Self { diff --git a/src/utils/syncqueue.rs b/src/utils/syncqueue.rs index 3803865c..7d8d17ce 100644 --- a/src/utils/syncqueue.rs +++ b/src/utils/syncqueue.rs @@ -1,6 +1,7 @@ use crate::utils::ptr_ext::MutPtrExt; use std::cell::UnsafeCell; use std::collections::VecDeque; +use std::mem; pub struct SyncQueue { el: UnsafeCell>, @@ -28,4 +29,10 @@ impl SyncQueue { pub fn is_empty(&self) -> bool { unsafe { self.el.get().deref_mut().is_empty() } } + + pub fn swap(&self, queue: &mut VecDeque) { + unsafe { + mem::swap(self.el.get().deref_mut(), queue); + } + } } diff --git a/src/xcon.rs b/src/xcon.rs index 440e19b1..22a55fe9 100644 --- a/src/xcon.rs +++ b/src/xcon.rs @@ -1,15 +1,18 @@ use crate::async_engine::SpawnedFuture; use crate::utils::bufio::{BufIo, BufIoError, BufIoMessage}; use crate::utils::oserror::OsError; +use crate::utils::vec_ext::VecExt; use crate::wire_xcon::{ - Extension, GetInputFocus, ListExtensions, QueryExtension, RenderQueryPictFormats, Setup, - EXTENSIONS, + CreateGC, CreatePixmap, Extension, FreeGC, FreePixmap, GetInputFocus, GetProperty, + ListExtensions, PutImage, QueryExtension, RenderCreateCursor, RenderCreatePicture, + RenderQueryPictFormats, Setup, EXTENSIONS, }; -use crate::xcon::consts::RENDER_PICT_TYPE_DIRECT; +use crate::xcon::consts::{IMAGE_FORMAT_Z_PIXMAP, RENDER_PICT_TYPE_DIRECT}; pub use crate::xcon::formatter::Formatter; use crate::xcon::incoming::handle_incoming; use crate::xcon::outgoing::handle_outgoing; pub use crate::xcon::parser::Parser; +use crate::xcon::wire_type::SendEvent; pub use crate::xcon::wire_type::{Message, Request, XEvent}; use crate::xcon::xauthority::{XAuthority, LOCAL, MIT_MAGIC_COOKIE}; use crate::{AsyncEngine, AsyncError, AsyncQueue, CloneCell, ErrorFmt, NumCell, Phase}; @@ -95,6 +98,24 @@ pub enum XconError { QueryPictFormats(#[source] Box), #[error("The server does not support the picture format for cursors")] CursorFormatNotSupported, + #[error("Could not create a pixmap")] + CreatePixmap(#[source] Box), + #[error("Could not create a graphics context")] + CreateGc(#[source] Box), + #[error("Could not upload an image")] + PutImage(#[source] Box), + #[error("Could not create a picture")] + CreatePicture(#[source] Box), + #[error("Could not create a cursor")] + CreateCursor(#[source] Box), + #[error("Property has an invalid type")] + InvalidPropertyType, + #[error("Property has an invalid format. Expected: {0}; Actual: {1}")] + InvalidPropertyFormat(u8, u8), + #[error("Length of the property data is not a multiple of its format")] + IrregularPropertyLength, + #[error("The property is not set")] + PropertyUnavailable, } #[derive(Debug)] @@ -107,6 +128,7 @@ struct ExtensionIdRange { #[derive(Default, Debug)] struct ExtensionData { opcodes: [Option; EXTENSIONS.len()], + first_event: [Option; EXTENSIONS.len()], ext_by_opcode: AHashMap, events: Vec, errors: Vec, @@ -124,6 +146,12 @@ pub struct Xcon { xid_max: u32, } +impl Drop for Xcon { + fn drop(&mut self) { + self.data.kill(); + } +} + struct XconData { bufio: Rc, next_serial: NumCell, @@ -147,6 +175,7 @@ pub struct Event { ext: Option, code: u16, buf: Vec, + serial: u64, } impl Deref for Event { @@ -166,6 +195,10 @@ impl Event { self.code } + pub fn serial(&self) -> u64 { + self.serial + } + pub fn parse<'a, M: Message<'a>>(&'a self) -> Result { let mut parser = Parser::new(&self.buf, vec![]); M::deserialize(&mut parser) @@ -448,30 +481,178 @@ impl Xcon { } pub fn call<'a, T: Request<'a>>(self: &Rc, t: &T) -> AsyncReply { - self.data.call(t, &self.extensions) + self.data.call_with_serial(t, &self.extensions).0 } - pub async fn find_cursor_format(self: &Rc) -> Result { - let res = match self.call(&RenderQueryPictFormats {}).await { - Ok(r) => r, - Err(e) => return Err(XconError::QueryPictFormats(Box::new(e))), - }; - for format in res.get().formats.iter() { - let valid = format.ty == RENDER_PICT_TYPE_DIRECT - && format.depth == 32 - && format.direct.red_shift == 16 - && format.direct.red_mask == 0xff - && format.direct.green_shift == 8 - && format.direct.green_mask == 0xff - && format.direct.blue_shift == 0 - && format.direct.blue_mask == 0xff - && format.direct.alpha_shift == 24 - && format.direct.alpha_mask == 0xff; - if valid { - return Ok(format.id); + pub fn call_with_serial<'a, T: Request<'a>>( + self: &Rc, + t: &T, + ) -> (AsyncReply, u64) { + self.data.call_with_serial(t, &self.extensions) + } + + pub fn send_event<'a, T: XEvent<'a>>( + self: &Rc, + propagate: bool, + destination: u32, + event_mask: u32, + t: &T, + ) -> AsyncReply<()> { + self.data + .send_event(t, &self.extensions, propagate, destination, event_mask) + } + + pub async fn get_property( + self: &Rc, + window: u32, + property: u32, + ty: u32, + buf: &mut Vec, + ) -> Result { + let len = buf.len(); + match self.get_property2(window, property, ty, buf).await { + Ok(n) => Ok(n), + Err(e) => { + buf.truncate(len); + Err(e) } } - Err(XconError::CursorFormatNotSupported) + } + + async fn get_property2( + self: &Rc, + window: u32, + property: u32, + ty: u32, + buf: &mut Vec, + ) -> Result { + let mut gp = GetProperty { + delete: 0, + window, + property, + ty, + long_offset: 0, + long_length: 128, + }; + let format = mem::size_of::() as u8 * 8; + loop { + let res = self.call(&gp).await?; + let res = res.get(); + if res.format == 0 { + return Err(XconError::PropertyUnavailable); + } + if gp.ty != 0 && gp.ty != res.ty { + return Err(XconError::InvalidPropertyType); + } + gp.ty = res.ty; + if res.format != format { + return Err(XconError::InvalidPropertyFormat(format, res.format)); + } + if res.data.len() % mem::size_of::() != 0 { + return Err(XconError::IrregularPropertyLength); + } + let len = res.data.len() / mem::size_of::(); + buf.reserve(len); + let (_, uninit) = buf.split_at_spare_mut_bytes_ext(); + uninit[..res.data.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(res.data)); + unsafe { + buf.set_len(buf.len() + len); + } + if res.bytes_after == 0 { + return Ok(res.ty); + } + gp.long_offset += gp.long_length; + } + } + + pub async fn create_cursor( + self: &Rc, + pixels: &[Cell], + width: i32, + height: i32, + xhot: i32, + yhot: i32, + ) -> Result { + let cursor_format = 'cursor_format: { + let res = match self.call(&RenderQueryPictFormats {}).await { + Ok(r) => r, + Err(e) => return Err(XconError::QueryPictFormats(Box::new(e))), + }; + for format in res.get().formats.iter() { + let valid = format.ty == RENDER_PICT_TYPE_DIRECT + && format.depth == 32 + && format.direct.red_shift == 16 + && format.direct.red_mask == 0xff + && format.direct.green_shift == 8 + && format.direct.green_mask == 0xff + && format.direct.blue_shift == 0 + && format.direct.blue_mask == 0xff + && format.direct.alpha_shift == 24 + && format.direct.alpha_mask == 0xff; + if valid { + break 'cursor_format format.id; + } + } + return Err(XconError::CursorFormatNotSupported); + }; + let pixmap = self.generate_id()?; + let gc = self.generate_id()?; + let picture = self.generate_id()?; + let cursor = self.generate_id()?; + let create_pixmap = self.call(&CreatePixmap { + depth: 32, + pid: pixmap, + drawable: self.setup.get().screens[0].root, + width: width as _, + height: height as _, + }); + let create_gc = self.call(&CreateGC { + cid: gc, + drawable: pixmap, + values: Default::default(), + }); + let put_image = self.call(&PutImage { + format: IMAGE_FORMAT_Z_PIXMAP, + drawable: pixmap, + gc, + width: width as _, + height: height as _, + dst_x: 0, + dst_y: 0, + left_pad: 0, + depth: 32, + data: unsafe { mem::transmute(pixels) }, + }); + self.call(&FreeGC { gc }); + let create_picture = self.call(&RenderCreatePicture { + pid: picture, + drawable: pixmap, + format: cursor_format, + values: Default::default(), + }); + self.call(&FreePixmap { pixmap }); + let create_cursor = self.call(&RenderCreateCursor { + cid: cursor, + source: picture, + x: xhot as _, + y: yhot as _, + }); + if let Err(e) = create_pixmap.await { + return Err(XconError::CreatePixmap(Box::new(e))); + } + if let Err(e) = create_gc.await { + return Err(XconError::CreateGc(Box::new(e))); + } + if let Err(e) = put_image.await { + return Err(XconError::PutImage(Box::new(e))); + } + if let Err(e) = create_picture.await { + return Err(XconError::CreatePicture(Box::new(e))); + } + if let Err(e) = create_cursor.await { + return Err(XconError::CreateCursor(Box::new(e))); + } + Ok(cursor) } } @@ -489,35 +670,86 @@ impl XconData { } } + #[cold] + fn dead>(self: &Rc) -> AsyncReply { + AsyncReply { + slot: Rc::new(AsyncReplySlot { + data: Cell::new(Some(Err(XconError::Dead))), + waker: Cell::new(None), + }), + xorg: self.clone(), + } + } + + #[cold] + fn ext_unavailable>(self: &Rc, idx: usize) -> AsyncReply { + AsyncReply { + slot: Rc::new(AsyncReplySlot { + data: Cell::new(Some(Err(XconError::ExtensionUnavailable( + EXTENSIONS[idx].name(), + )))), + waker: Cell::new(None), + }), + xorg: self.clone(), + } + } + + fn send_event<'a, T: XEvent<'a>>( + self: &Rc, + t: &T, + extensions: &ExtensionData, + propagate: bool, + destination: u32, + event_mask: u32, + ) -> AsyncReply<()> { + if self.dead.get() { + return self.dead(); + } + let first_event = match T::EXTENSION { + None => 0, + Some(idx) => match extensions.first_event[idx] { + Some(o) => o, + _ => return self.ext_unavailable(idx), + }, + }; + let mut fds = vec![]; + let mut buf = self.bufio.buf(); + let mut formatter = Formatter::new(&mut fds, &mut buf, 0); + let se = SendEvent { + propagate: propagate as u8, + destination, + event_mask, + }; + se.serialize(&mut formatter); + t.serialize(&mut formatter); + formatter.pad_to(44); + formatter.write_request_length(); + buf[12] = first_event + T::OPCODE as u8; + self.need_sync.set(true); + self.send(fds, buf).0 + } + fn call<'a, T: Request<'a>>( self: &Rc, t: &T, extensions: &ExtensionData, ) -> AsyncReply { + self.call_with_serial(t, extensions).0 + } + + fn call_with_serial<'a, T: Request<'a>>( + self: &Rc, + t: &T, + extensions: &ExtensionData, + ) -> (AsyncReply, u64) { if self.dead.get() { - return AsyncReply { - slot: Rc::new(AsyncReplySlot { - data: Cell::new(Some(Err(XconError::Dead))), - waker: Cell::new(None), - }), - xorg: self.clone(), - }; + return (self.dead(), 0); } let opcode = match T::EXTENSION { None => 0, Some(idx) => match extensions.opcodes[idx] { Some(o) => o, - _ => { - return AsyncReply { - slot: Rc::new(AsyncReplySlot { - data: Cell::new(Some(Err(XconError::ExtensionUnavailable( - EXTENSIONS[idx].name(), - )))), - waker: Cell::new(None), - }), - xorg: self.clone(), - } - } + _ => return (self.ext_unavailable(idx), 0), }, }; let mut fds = vec![]; @@ -525,21 +757,31 @@ impl XconData { let mut formatter = Formatter::new(&mut fds, &mut buf, opcode); t.serialize(&mut formatter); formatter.write_request_length(); + self.need_sync.set(T::IS_VOID); + self.send(fds, buf) + } + + fn send>( + self: &Rc, + fds: Vec>, + buf: Vec, + ) -> (AsyncReply, u64) { self.bufio.send(BufIoMessage { fds, buf }); let slot = Rc::new(AsyncReplySlot { data: Cell::new(None), waker: Cell::new(None), }); + let serial = self.next_serial.fetch_add(1); let handler = Box::new(AsyncReplyHandler { - serial: self.next_serial.fetch_add(1), + serial, slot: Rc::downgrade(&slot), }); self.reply_handlers.borrow_mut().push_back(handler); - self.need_sync.set(T::IS_VOID); - AsyncReply { + let rep = AsyncReply { slot, xorg: self.clone(), - } + }; + (rep, serial) } fn send_sync(&self) { @@ -590,6 +832,7 @@ impl XconData { } if let Some(e) = e { ed.opcodes[e as usize] = Some(data.major_opcode); + ed.first_event[e as usize] = Some(data.first_event); ed.ext_by_opcode.insert(data.major_opcode, e); } } @@ -617,3 +860,9 @@ fn parse_display() -> Result { }; Ok(num) } + +pub unsafe trait PropertyType {} + +unsafe impl PropertyType for u8 {} +unsafe impl PropertyType for u16 {} +unsafe impl PropertyType for u32 {} diff --git a/src/xcon/consts.rs b/src/xcon/consts.rs index 0a81846d..fb2c12a4 100644 --- a/src/xcon/consts.rs +++ b/src/xcon/consts.rs @@ -7,10 +7,76 @@ pub const INPUT_DEVICE_ALL_MASTER: u16 = 1; pub const WINDOW_CLASS_INPUT_OUTPUT: u16 = 1; -pub const PROP_MODE_REPLACE: u8 = 0; - -pub const ATOM_WM_CLASS: u32 = 67; +pub const ATOM_NONE: u32 = 0; +pub const ATOM_ANY: u32 = 0; +pub const ATOM_PRIMARY: u32 = 1; +pub const ATOM_SECONDARY: u32 = 2; +pub const ATOM_ARC: u32 = 3; +pub const ATOM_ATOM: u32 = 4; +pub const ATOM_BITMAP: u32 = 5; +pub const ATOM_CARDINAL: u32 = 6; +pub const ATOM_COLORMAP: u32 = 7; +pub const ATOM_CURSOR: u32 = 8; +pub const ATOM_CUT_BUFFER0: u32 = 9; +pub const ATOM_CUT_BUFFER1: u32 = 10; +pub const ATOM_CUT_BUFFER2: u32 = 11; +pub const ATOM_CUT_BUFFER3: u32 = 12; +pub const ATOM_CUT_BUFFER4: u32 = 13; +pub const ATOM_CUT_BUFFER5: u32 = 14; +pub const ATOM_CUT_BUFFER6: u32 = 15; +pub const ATOM_CUT_BUFFER7: u32 = 16; +pub const ATOM_DRAWABLE: u32 = 17; +pub const ATOM_FONT: u32 = 18; +pub const ATOM_INTEGER: u32 = 19; +pub const ATOM_PIXMAP: u32 = 20; +pub const ATOM_POINT: u32 = 21; +pub const ATOM_RECTANGLE: u32 = 22; +pub const ATOM_RESOURCE_MANAGER: u32 = 23; +pub const ATOM_RGB_COLOR_MAP: u32 = 24; +pub const ATOM_RGB_BEST_MAP: u32 = 25; +pub const ATOM_RGB_BLUE_MAP: u32 = 26; +pub const ATOM_RGB_DEFAULT_MAP: u32 = 27; +pub const ATOM_RGB_GRAY_MAP: u32 = 28; +pub const ATOM_RGB_GREEN_MAP: u32 = 29; +pub const ATOM_RGB_RED_MAP: u32 = 30; pub const ATOM_STRING: u32 = 31; +pub const ATOM_VISUALID: u32 = 32; +pub const ATOM_WINDOW: u32 = 33; +pub const ATOM_WM_COMMAND: u32 = 34; +pub const ATOM_WM_HINTS: u32 = 35; +pub const ATOM_WM_CLIENT_MACHINE: u32 = 36; +pub const ATOM_WM_ICON_NAME: u32 = 37; +pub const ATOM_WM_ICON_SIZE: u32 = 38; +pub const ATOM_WM_NAME: u32 = 39; +pub const ATOM_WM_NORMAL_HINTS: u32 = 40; +pub const ATOM_WM_SIZE_HINTS: u32 = 41; +pub const ATOM_WM_ZOOM_HINTS: u32 = 42; +pub const ATOM_MIN_SPACE: u32 = 43; +pub const ATOM_NORM_SPACE: u32 = 44; +pub const ATOM_MAX_SPACE: u32 = 45; +pub const ATOM_END_SPACE: u32 = 46; +pub const ATOM_SUPERSCRIPT_X: u32 = 47; +pub const ATOM_SUPERSCRIPT_Y: u32 = 48; +pub const ATOM_SUBSCRIPT_X: u32 = 49; +pub const ATOM_SUBSCRIPT_Y: u32 = 50; +pub const ATOM_UNDERLINE_POSITION: u32 = 51; +pub const ATOM_UNDERLINE_THICKNESS: u32 = 52; +pub const ATOM_STRIKEOUT_ASCENT: u32 = 53; +pub const ATOM_STRIKEOUT_DESCENT: u32 = 54; +pub const ATOM_ITALIC_ANGLE: u32 = 55; +pub const ATOM_X_HEIGHT: u32 = 56; +pub const ATOM_QUAD_WIDTH: u32 = 57; +pub const ATOM_WEIGHT: u32 = 58; +pub const ATOM_POINT_SIZE: u32 = 59; +pub const ATOM_RESOLUTION: u32 = 60; +pub const ATOM_COPYRIGHT: u32 = 61; +pub const ATOM_NOTICE: u32 = 62; +pub const ATOM_FONT_NAME: u32 = 63; +pub const ATOM_FAMILY_NAME: u32 = 64; +pub const ATOM_FULL_NAME: u32 = 65; +pub const ATOM_CAP_HEIGHT: u32 = 66; +pub const ATOM_WM_CLASS: u32 = 67; +pub const ATOM_WM_TRANSIENT_FOR: u32 = 68; pub const EVENT_MASK_NO_EVENT: u32 = 0; pub const EVENT_MASK_KEY_PRESS: u32 = 1; @@ -111,3 +177,77 @@ pub const COMPOSITE_REDIRECT_MANUAL: u8 = 1; pub const RENDER_PICT_TYPE_INDEXED: u8 = 0; pub const RENDER_PICT_TYPE_DIRECT: u8 = 1; + +pub const PROP_MODE_REPLACE: u8 = 0; +pub const PROP_MODE_PREPEND: u8 = 1; +pub const PROP_MODE_APPEND: u8 = 2; + +pub const ICCCM_WM_HINT_INPUT: i32 = 1 << 0; +pub const ICCCM_WM_HINT_STATE: i32 = 1 << 1; +pub const ICCCM_WM_HINT_ICON_PIXMAP: i32 = 1 << 2; +pub const ICCCM_WM_HINT_ICON_WINDOW: i32 = 1 << 3; +pub const ICCCM_WM_HINT_ICON_POSITION: i32 = 1 << 4; +pub const ICCCM_WM_HINT_ICON_MASK: i32 = 1 << 5; +pub const ICCCM_WM_HINT_WINDOW_GROUP: i32 = 1 << 6; +pub const ICCCM_WM_HINT_X_URGENCY: i32 = 1 << 8; + +pub const ICCCM_SIZE_HINT_US_POSITION: u32 = 1 << 0; +pub const ICCCM_SIZE_HINT_US_SIZE: u32 = 1 << 1; +pub const ICCCM_SIZE_HINT_P_POSITION: u32 = 1 << 2; +pub const ICCCM_SIZE_HINT_P_SIZE: u32 = 1 << 3; +pub const ICCCM_SIZE_HINT_P_MIN_SIZE: u32 = 1 << 4; +pub const ICCCM_SIZE_HINT_P_MAX_SIZE: u32 = 1 << 5; +pub const ICCCM_SIZE_HINT_P_RESIZE_INC: u32 = 1 << 6; +pub const ICCCM_SIZE_HINT_P_ASPECT: u32 = 1 << 7; +pub const ICCCM_SIZE_HINT_BASE_SIZE: u32 = 1 << 8; +pub const ICCCM_SIZE_HINT_P_WIN_GRAVITY: u32 = 1 << 9; + +pub const MWM_HINTS_FLAGS_FIELD: usize = 0; +pub const MWM_HINTS_DECORATIONS_FIELD: usize = 2; +pub const MWM_HINTS_DECORATIONS: u32 = 1 << 1; +pub const MWM_DECOR_ALL: u32 = 1 << 0; +pub const MWM_DECOR_BORDER: u32 = 1 << 1; +pub const MWM_DECOR_TITLE: u32 = 1 << 3; + +pub const RES_CLIENT_ID_MASK_CLIENT_XID: u32 = 1; +pub const RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID: u32 = 2; + +pub const INPUT_FOCUS_NONE: u8 = 0; +pub const INPUT_FOCUS_POINTER_ROOT: u8 = 1; +pub const INPUT_FOCUS_PARENT: u8 = 2; +pub const INPUT_FOCUS_FOLLOW_KEYBOARD: u8 = 3; + +pub const NOTIFY_MODE_NORMAL :u8 = 0; +pub const NOTIFY_MODE_GRAB :u8 = 1; +pub const NOTIFY_MODE_UNGRAB :u8 = 2; +pub const NOTIFY_MODE_WHILE_GRABBED :u8 = 3; + +pub const NOTIFY_DETAIL_ANCESTOR : u8 = 0; +pub const NOTIFY_DETAIL_VIRTUAL : u8 = 1; +pub const NOTIFY_DETAIL_INFERIOR : u8 = 2; +pub const NOTIFY_DETAIL_NONLINEAR : u8 = 3; +pub const NOTIFY_DETAIL_NONLINEAR_VIRTUAL : u8 = 4; +pub const NOTIFY_DETAIL_POINTER : u8 = 5; +pub const NOTIFY_DETAIL_POINTER_ROOT : u8 = 6; +pub const NOTIFY_DETAIL_NONE : u8 = 7; + +pub const ICCCM_WM_STATE_WITHDRAWN : u32 = 0; +pub const ICCCM_WM_STATE_NORMAL : u32 = 1; +pub const ICCCM_WM_STATE_ICONIC : u32 = 3; + +pub const _NET_WM_STATE_REMOVE: u32 = 0; +pub const _NET_WM_STATE_ADD: u32 = 1; +pub const _NET_WM_STATE_TOGGLE: u32 = 2; + +pub const _NET_WM_MOVERESIZE_SIZE_TOPLEFT : u32 = 0; +pub const _NET_WM_MOVERESIZE_SIZE_TOP : u32 = 1; +pub const _NET_WM_MOVERESIZE_SIZE_TOPRIGHT : u32 = 2; +pub const _NET_WM_MOVERESIZE_SIZE_RIGHT : u32 = 3; +pub const _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT : u32 = 4; +pub const _NET_WM_MOVERESIZE_SIZE_BOTTOM : u32 = 5; +pub const _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT : u32 = 6; +pub const _NET_WM_MOVERESIZE_SIZE_LEFT : u32 = 7; +pub const _NET_WM_MOVERESIZE_MOVE : u32 = 8; +pub const _NET_WM_MOVERESIZE_SIZE_KEYBOARD : u32 = 9; +pub const _NET_WM_MOVERESIZE_MOVE_KEYBOARD : u32 = 10; +pub const _NET_WM_MOVERESIZE_CANCEL : u32 = 11; diff --git a/src/xcon/formatter.rs b/src/xcon/formatter.rs index db35787e..32977173 100644 --- a/src/xcon/formatter.rs +++ b/src/xcon/formatter.rs @@ -26,6 +26,14 @@ impl<'a> Formatter<'a> { self.buf.extend_from_slice(&BUF[..pad]); } + pub fn pad_to(&mut self, size: usize) { + static BUF: [u8; 8] = [0; 8]; + while self.buf.len() < size { + let len = (size - self.buf.len()).min(8); + self.buf.extend_from_slice(&BUF[..len]); + } + } + pub fn align(&mut self, alignment: usize) { static BUF: [u8; 8] = [0; 8]; let len = self.buf.len().wrapping_neg() & (alignment - 1); diff --git a/src/xcon/incoming.rs b/src/xcon/incoming.rs index 0a6f4ee0..fbcbadc4 100644 --- a/src/xcon/incoming.rs +++ b/src/xcon/incoming.rs @@ -202,6 +202,7 @@ impl Incoming { ext, code, buf: mem::take(&mut msg_buf), + serial, }); } } diff --git a/src/xcon/wire_type.rs b/src/xcon/wire_type.rs index fac7e1ae..b9e526b1 100644 --- a/src/xcon/wire_type.rs +++ b/src/xcon/wire_type.rs @@ -34,6 +34,7 @@ pub trait Request<'a>: Message<'a> { } pub trait XEvent<'a>: Message<'a> { + const EXTENSION: Option; const OPCODE: u16; } @@ -116,3 +117,38 @@ unsafe impl<'a> Message<'a> for Rc { parser.read_fd() } } + +#[derive(Debug, Clone)] +pub(super) struct SendEvent { + pub propagate: u8, + pub destination: u32, + pub event_mask: u32, +} + +unsafe impl<'a> Message<'a> for SendEvent { + type Generic<'b> = SendEvent; + const IS_POD: bool = false; + const HAS_FDS: bool = false; + + fn serialize(&self, formatter: &mut Formatter) { + { + let propagate_bytes = self.propagate.to_ne_bytes(); + let destination_bytes = self.destination.to_ne_bytes(); + let event_mask_bytes = self.event_mask.to_ne_bytes(); + formatter.write_bytes(&[ + 25, + propagate_bytes[0], + 0, + 0, + destination_bytes[0], + destination_bytes[1], + destination_bytes[2], + destination_bytes[3], + event_mask_bytes[0], + event_mask_bytes[1], + event_mask_bytes[2], + event_mask_bytes[3], + ]); + } + } +} diff --git a/src/xwayland.rs b/src/xwayland.rs index b8a85451..61393800 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -3,7 +3,7 @@ mod xwm; use crate::client::ClientError; use crate::forker::ForkerProxy; -use crate::ifs::wl_surface::xwindow::Xwindow; +use crate::ifs::wl_surface::xwindow::{Xwindow, XwindowData}; use crate::ifs::wl_surface::WlSurface; use crate::utils::oserror::OsError; use crate::utils::tri::Try; @@ -60,16 +60,12 @@ enum XWaylandError { SelectRootEvents(#[source] XconError), #[error("Could not create the WM window")] CreateXWindow(#[source] XconError), - #[error("Could not acquire a selection")] - SelectionOwner(#[source] XconError), #[error("Could not set the cursor of the root window")] SetCursor(#[source] XconError), #[error("composite_redirect_subwindows failed")] CompositeRedirectSubwindows(#[source] XconError), #[error("Could not spawn the Xwayland client")] SpawnClient(#[source] ClientError), - #[error("Could not map a window")] - MapWindow(#[source] XconError), #[error("An unspecified XconError occurred")] XconError(#[from] XconError), } @@ -263,4 +259,5 @@ pub enum XWaylandEvent { SurfaceCreated(Rc), SurfaceDestroyed(WlSurfaceId), Configure(Rc), + Activate(Rc), } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index b71b3333..70bd3807 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -1,110 +1,120 @@ use crate::client::Client; -use crate::ifs::wl_surface::xwindow::{Xwindow, XwindowData}; +use crate::ifs::wl_surface::xwindow::{XInputModel, Xwindow, XwindowData}; use crate::ifs::wl_surface::WlSurface; use crate::rect::Rect; +use crate::utils::bitflags::BitflagsExt; use crate::wire::WlSurfaceId; -use crate::wire_xcon::{ - ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows, ConfigureNotify, - ConfigureRequest, ConfigureWindow, ConfigureWindowValues, CreateGC, CreateNotify, CreatePixmap, - CreateWindow, CreateWindowValues, DestroyNotify, FreeGC, FreePixmap, InternAtom, MapRequest, - MapWindow, PutImage, RenderCreateCursor, RenderCreatePicture, SetSelectionOwner, -}; -use crate::xcon::consts::{ - COMPOSITE_REDIRECT_MANUAL, EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_SUBSTRUCTURE_NOTIFY, - EVENT_MASK_SUBSTRUCTURE_REDIRECT, IMAGE_FORMAT_Z_PIXMAP, WINDOW_CLASS_INPUT_OUTPUT, -}; -use crate::xcon::{Event, XEvent, Xcon}; +use crate::wire_xcon::{ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows, ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues, CreateNotify, CreateWindow, CreateWindowValues, DestroyNotify, FocusIn, GetGeometry, InternAtom, KillClient, MapNotify, MapRequest, MapWindow, PropertyNotify, ResClientIdSpec, ResQueryClientIds, SetInputFocus, SetSelectionOwner, UnmapNotify}; +use crate::xcon::consts::{_NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE, ATOM_ATOM, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME, ATOM_WM_SIZE_HINTS, ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, EVENT_MASK_FOCUS_CHANGE, EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_SUBSTRUCTURE_NOTIFY, EVENT_MASK_SUBSTRUCTURE_REDIRECT, ICCCM_WM_HINT_INPUT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL, ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, MWM_HINTS_DECORATIONS_FIELD, MWM_HINTS_FLAGS_FIELD, NOTIFY_DETAIL_POINTER, NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, PROP_MODE_REPLACE, RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, WINDOW_CLASS_INPUT_OUTPUT}; +use crate::xcon::{Event, XEvent, Xcon, XconError}; use crate::xwayland::{XWaylandError, XWaylandEvent}; use crate::{AsyncQueue, ErrorFmt, State}; -use ahash::AHashMap; +use ahash::{AHashMap, AHashSet}; use futures_util::{select, FutureExt}; +use smallvec::SmallVec; +use std::borrow::Cow; use std::mem; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; +use bstr::ByteSlice; use uapi::OwnedFd; +use crate::tree::Node; +use crate::utils::linkedlist::LinkedList; -atom_manager! { +atoms! { Atoms; - WL_SURFACE_ID, - WM_DELETE_WINDOW, - WM_PROTOCOLS, - WM_HINTS, - WM_NORMAL_HINTS, - WM_SIZE_HINTS, - WM_WINDOW_ROLE, - MOTIF_WM_HINTS, - UTF8_STRING, - WM_S0, - NET_SUPPORTED, - NET_WM_CM_S0, - NET_WM_PID, - NET_WM_NAME, - NET_WM_STATE, - NET_WM_WINDOW_TYPE, - WM_TAKE_FOCUS, - WINDOW, - NET_ACTIVE_WINDOW, - NET_WM_MOVERESIZE, - NET_SUPPORTING_WM_CHECK, - NET_WM_STATE_FOCUSED, - NET_WM_STATE_MODAL, - NET_WM_STATE_FULLSCREEN, - NET_WM_STATE_MAXIMIZED_VERT, - NET_WM_STATE_MAXIMIZED_HORZ, - NET_WM_STATE_HIDDEN, - NET_WM_PING, - WM_CHANGE_STATE, - WM_STATE, CLIPBOARD, - PRIMARY, - WL_SELECTION, - TARGETS, CLIPBOARD_MANAGER, + DELETE, INCR, + _MOTIF_WM_HINTS, + _NET_ACTIVE_WINDOW, + _NET_CLIENT_LIST, + _NET_CLIENT_LIST_STACKING, + _NET_STARTUP_ID, + _NET_STARTUP_INFO, + _NET_STARTUP_INFO_BEGIN, + _NET_SUPPORTED, + _NET_SUPPORTING_WM_CHECK, + _NET_WM_CM_S0, + _NET_WM_MOVERESIZE, + _NET_WM_NAME, + _NET_WM_PID, + _NET_WM_PING, + _NET_WM_STATE, + _NET_WM_STATE_FOCUSED, + _NET_WM_STATE_FULLSCREEN, + _NET_WM_STATE_HIDDEN, + _NET_WM_STATE_MAXIMIZED_HORZ, + _NET_WM_STATE_MAXIMIZED_VERT, + _NET_WM_STATE_MODAL, + _NET_WM_WINDOW_TYPE, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DND, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_NORMAL, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_UTILITY, + PRIMARY, + TARGETS, TEXT, TIMESTAMP, - DELETE, - NET_STARTUP_ID, - NET_STARTUP_INFO, - NET_STARTUP_INFO_BEGIN, - NET_WM_WINDOW_TYPE_NORMAL, - NET_WM_WINDOW_TYPE_UTILITY, - NET_WM_WINDOW_TYPE_TOOLTIP, - NET_WM_WINDOW_TYPE_DND, - NET_WM_WINDOW_TYPE_DROPDOWN_MENU, - NET_WM_WINDOW_TYPE_POPUP_MENU, - NET_WM_WINDOW_TYPE_COMBO, - NET_WM_WINDOW_TYPE_MENU, - NET_WM_WINDOW_TYPE_NOTIFICATION, - NET_WM_WINDOW_TYPE_SPLASH, - DND_SELECTION, - DND_AWARE, - DND_STATUS, - DND_POSITION, - DND_ENTER, - DND_LEAVE, - DND_DROP, - DND_FINISHED, - DND_PROXY, - DND_TYPE_LIST, - DND_ACTION_MOVE, - DND_ACTION_COPY, - DND_ACTION_ASK, - DND_ACTION_PRIVATE, - NET_CLIENT_LIST, - NET_CLIENT_LIST_STACKING, + UTF8_STRING, + WINDOW, + _WL_SELECTION, + WL_SURFACE_ID, + WM_CHANGE_STATE, + WM_DELETE_WINDOW, + WM_HINTS, + WM_NORMAL_HINTS, + WM_PROTOCOLS, + WM_S0, + WM_SIZE_HINTS, + WM_STATE, + WM_TAKE_FOCUS, + WM_WINDOW_ROLE, + XdndActionAsk, + XdndActionCopy, + XdndActionMove, + XdndActionPrivate, + XdndAware, + XdndDrop, + XdndEnter, + XdndFinished, + XdndLeave, + XdndPosition, + XdndProxy, + XdndSelection, + XdndStatus, + XdndTypeList, } pub struct Wm { state: Rc, c: Rc, atoms: Atoms, - _root: u32, - _xwin: u32, + never_focus: AHashSet, + root: u32, + xwin: u32, client: Rc, windows: AHashMap>, windows_by_surface_id: AHashMap>, queue: Rc>, + focus_window: Option>, + last_input_serial: u64, + + stack_list: LinkedList>, + num_stacked: usize, + + map_list: LinkedList>, + num_mapped: usize, } impl Drop for Wm { @@ -132,6 +142,19 @@ impl Wm { Ok(a) => a, Err(e) => return Err(XWaylandError::LoadAtoms(e)), }; + let never_focus = { + let mut nf = AHashSet::new(); + nf.insert(atoms._NET_WM_WINDOW_TYPE_COMBO); + nf.insert(atoms._NET_WM_WINDOW_TYPE_DND); + nf.insert(atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU); + nf.insert(atoms._NET_WM_WINDOW_TYPE_MENU); + nf.insert(atoms._NET_WM_WINDOW_TYPE_NOTIFICATION); + nf.insert(atoms._NET_WM_WINDOW_TYPE_POPUP_MENU); + nf.insert(atoms._NET_WM_WINDOW_TYPE_SPLASH); + nf.insert(atoms._NET_WM_WINDOW_TYPE_TOOLTIP); + nf.insert(atoms._NET_WM_WINDOW_TYPE_UTILITY); + nf + }; let root = c.setup().screens[0].root; { let events = 0 @@ -159,9 +182,10 @@ impl Wm { } } let xwin = { + let xwin = c.generate_id()?; let cw = CreateWindow { depth: 0, - wid: c.generate_id()?, + wid: xwin, parent: root, x: 0, y: 0, @@ -175,20 +199,76 @@ impl Wm { if let Err(e) = c.call(&cw).await { return Err(XWaylandError::CreateXWindow(e)); } - cw.wid - }; - { - let sso = SetSelectionOwner { + // c.call(&ChangeProperty { + // mode: PROP_MODE_REPLACE, + // window: xwin, + // property: atoms._NET_WM_NAME, + // ty: atoms.UTF8_STRING, + // format: 8, + // data: "jay wm".as_bytes(), + // }); + // c.call(&ChangeProperty { + // mode: PROP_MODE_REPLACE, + // window: root, + // property: atoms._NET_SUPPORTING_WM_CHECK, + // ty: ATOM_WINDOW, + // format: 32, + // data: uapi::as_bytes(&xwin), + // }); + // c.call(&ChangeProperty { + // mode: PROP_MODE_REPLACE, + // window: xwin, + // property: atoms._NET_SUPPORTING_WM_CHECK, + // ty: ATOM_WINDOW, + // format: 32, + // data: uapi::as_bytes(&xwin), + // }); + c.call(&SetSelectionOwner { owner: xwin, selection: atoms.WM_S0, time: 0, - }; - if let Err(e) = c.call(&sso).await { - return Err(XWaylandError::SelectionOwner(e)); - } + }); + // c.call(&SetSelectionOwner { + // owner: xwin, + // selection: atoms._NET_WM_CM_S0, + // time: 0, + // }); + xwin + }; + { + // let supported_atoms = [ + // atoms._NET_WM_STATE, + // atoms._NET_ACTIVE_WINDOW, + // atoms._NET_WM_MOVERESIZE, + // atoms._NET_WM_STATE_FOCUSED, + // atoms._NET_WM_STATE_MODAL, + // atoms._NET_WM_STATE_FULLSCREEN, + // atoms._NET_WM_STATE_MAXIMIZED_VERT, + // atoms._NET_WM_STATE_MAXIMIZED_HORZ, + // atoms._NET_WM_STATE_HIDDEN, + // atoms._NET_CLIENT_LIST, + // atoms._NET_CLIENT_LIST_STACKING, + // ]; + // c.call(&ChangeProperty { + // mode: PROP_MODE_REPLACE, + // window: root, + // property: atoms._NET_SUPPORTED, + // ty: ATOM_ATOM, + // format: 32, + // data: uapi::as_bytes(&supported_atoms[..]), + // }); + } + { + c.call(&ChangeProperty { + mode: PROP_MODE_REPLACE, + window: root, + property: atoms._NET_ACTIVE_WINDOW, + ty: ATOM_ATOM, + format: 32, + data: uapi::as_bytes(&0u32), + }); } 'set_root_cursor: { - let cursor_format = c.find_cursor_format().await?; let cursors = match state.cursors.get() { Some(g) => g, _ => break 'set_root_cursor, @@ -197,80 +277,22 @@ impl Wm { Some(f) => f, _ => break 'set_root_cursor, }; - let pixmap = c.generate_id()?; - let gc = c.generate_id()?; - let picture = c.generate_id()?; - let cursor = c.generate_id()?; - let create_pixmap = c.call(&CreatePixmap { - depth: 32, - pid: pixmap, - drawable: root, - width: first.width as _, - height: first.height as _, - }); - let create_gc = c.call(&CreateGC { - cid: gc, - drawable: pixmap, - values: Default::default(), - }); - let put_image = c.call(&PutImage { - format: IMAGE_FORMAT_Z_PIXMAP, - drawable: pixmap, - gc, - width: first.width as _, - height: first.height as _, - dst_x: 0, - dst_y: 0, - left_pad: 0, - depth: 32, - data: unsafe { mem::transmute(&first.pixels[..]) }, - }); - c.call(&FreeGC { gc }); - let create_picture = c.call(&RenderCreatePicture { - pid: picture, - drawable: pixmap, - format: cursor_format, - values: Default::default(), - }); - c.call(&FreePixmap { pixmap }); - let create_cursor = c.call(&RenderCreateCursor { - cid: cursor, - source: picture, - x: first.xhot as _, - y: first.yhot as _, - }); - if let Err(e) = create_pixmap.await { - log::warn!( - "Could not create a pixmap for the root cursor: {}", - ErrorFmt(e) - ); - break 'set_root_cursor; - } - if let Err(e) = create_gc.await { - log::warn!( - "Could not create a graphics context for the root cursor: {}", - ErrorFmt(e) - ); - break 'set_root_cursor; - } - if let Err(e) = put_image.await { - log::warn!( - "Could not upload the image for the root cursor: {}", - ErrorFmt(e) - ); - break 'set_root_cursor; - } - if let Err(e) = create_picture.await { - log::warn!( - "Could not create a picture for the root cursor: {}", - ErrorFmt(e) - ); - break 'set_root_cursor; - } - if let Err(e) = create_cursor.await { - log::warn!("Could not create the root cursor: {}", ErrorFmt(e)); - break 'set_root_cursor; - } + let cursor = match c + .create_cursor( + &first.pixels, + first.width, + first.height, + first.xhot, + first.yhot, + ) + .await + { + Ok(c) => c, + Err(e) => { + log::warn!("Could not create a root cursor: {}", ErrorFmt(e)); + break 'set_root_cursor; + } + }; let cwa = ChangeWindowAttributes { window: root, values: CreateWindowValues { @@ -286,12 +308,19 @@ impl Wm { state: state.clone(), c, atoms, - _root: root, - _xwin: xwin, + never_focus, + root: root, + xwin: xwin, client, windows: Default::default(), windows_by_surface_id: Default::default(), queue, + focus_window: Default::default(), + last_input_serial: 0, + stack_list: Default::default(), + num_stacked: 0, + map_list: Default::default(), + num_mapped: 0 }) } @@ -306,18 +335,24 @@ impl Wm { async fn handle_xwayland_event(&mut self, e: XWaylandEvent) { match e { - XWaylandEvent::SurfaceCreated(event) => self.handle_xwayland_surface_created(event), + XWaylandEvent::SurfaceCreated(event) => { + self.handle_xwayland_surface_created(event).await + } 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, } } async fn handle_xwayland_configure(&mut self, window: Rc) { + if window.data.destroyed.get() { + return; + } self.send_configure(window).await; } async fn send_configure(&mut self, window: Rc) { - let extents = window.data.extents.get(); + let extents = window.data.info.extents.get(); let cw = ConfigureWindow { window: window.data.window_id, values: ConfigureWindowValues { @@ -334,7 +369,457 @@ impl Wm { } } - fn create_window(&mut self, data: &Rc, surface: Rc) { + async fn set_minimized(&self, data: &Rc, minimized: bool) { + data.info.minimized.set(minimized); + let state = match minimized { + true => ICCCM_WM_STATE_ICONIC, + false => ICCCM_WM_STATE_NORMAL, + }; + self.set_wm_state(data, state).await; + self.set_net_wm_state(data).await; + } + + async fn set_maximized(&self, data: &Rc, maximized: bool) { + data.info.maximized_vert.set(maximized); + data.info.maximized_horz.set(maximized); + self.set_net_wm_state(data).await; + } + + async fn set_fullscreen(&self, data: &Rc, fullscreen: bool) { + data.info.fullscreen.set(fullscreen); + self.set_net_wm_state(data).await; + } + + async fn send_wm_message(&self, window: &Rc, event_mask: u32, data: &[u32]) { + let event = ClientMessage { + format: 32, + window: window.window_id, + ty: self.atoms.WM_PROTOCOLS, + data, + }; + if let Err(e) = self + .c + .send_event(false, window.window_id, event_mask, &event) + .await + { + log::error!("Could not send WM_PROTOCOLS message: {}", ErrorFmt(e)); + } + } + + async fn focus_window(&mut self, window: Option<&Rc>) { + if let Some(old) = mem::replace(&mut self.focus_window, window.cloned()) { + self.set_net_wm_state(&old).await; + } + let window = match window { + Some(w) => w, + _ => { + if let Err(e) = self + .c + .call(&SetInputFocus { + revert_to: INPUT_FOCUS_POINTER_ROOT, + focus: 0, + time: 0, + }) + .await + { + log::error!("Could not unset pointer focus: {}", ErrorFmt(e)); + } + return; + } + }; + if window.info.override_redirect.get() { + return; + } + let accepts_input = window.info.icccm_hints.input.get() != 0; + let mask = if accepts_input { + EVENT_MASK_SUBSTRUCTURE_REDIRECT + } else { + 0 + }; + self.send_wm_message(window, mask, &[self.atoms.WM_TAKE_FOCUS, 0]) + .await; + if accepts_input { + let sif = SetInputFocus { + revert_to: INPUT_FOCUS_POINTER_ROOT, + focus: window.window_id, + time: 0, + }; + let (_, serial) = self.c.call_with_serial(&sif); + self.last_input_serial = serial; + } + self.set_net_wm_state(window).await; + } + + async fn set_net_wm_state(&self, data: &Rc) { + let mut args = SmallVec::<[_; 6]>::new(); + if data.info.modal.get() { + args.push(self.atoms._NET_WM_STATE_MODAL); + } + if data.info.fullscreen.get() { + args.push(self.atoms._NET_WM_STATE_FULLSCREEN); + } + if data.info.maximized_vert.get() { + args.push(self.atoms._NET_WM_STATE_MAXIMIZED_VERT); + } + if data.info.maximized_horz.get() { + args.push(self.atoms._NET_WM_STATE_MAXIMIZED_HORZ); + } + if data.info.minimized.get() { + args.push(self.atoms._NET_WM_STATE_HIDDEN); + } + if Some(data.window_id) == self.focus_window.as_ref().map(|w| w.window_id) { + args.push(self.atoms._NET_WM_STATE_FOCUSED); + } + let cp = ChangeProperty { + mode: PROP_MODE_REPLACE, + window: data.window_id, + property: self.atoms._NET_WM_STATE, + ty: ATOM_ATOM, + format: 32, + data: uapi::as_bytes(&args[..]), + }; + if let Err(e) = self.c.call(&cp).await { + log::error!("Could not set _NET_WM_STATE: {}", ErrorFmt(e)); + } + } + + fn compute_input_model(&self, data: &Rc) { + let has_wm_take_focus = data.info.protocols.contains(&self.atoms.WM_TAKE_FOCUS); + let accepts_input = data.info.icccm_hints.input.get() != 0; + let model = match (accepts_input, has_wm_take_focus) { + (false, false) => XInputModel::None, + (true, false) => XInputModel::Passive, + (true, true) => XInputModel::Local, + (false, true) => XInputModel::Global, + }; + data.info.input_model.set(model); + } + + async fn load_window_wm_window_role(&self, data: &Rc) { + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, self.atoms.WM_WINDOW_ROLE, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => {} + Ok(ty) => { + log::error!("WM_WINDOW_ROLE property has unexpected type {}", ty); + return; + } + Err(XconError::PropertyUnavailable) => { + data.info.role.borrow_mut().take(); + return; + } + Err(e) => { + log::error!("Could not retrieve WM_WINDOW_ROLE property: {}", ErrorFmt(e)); + return; + } + } + log::info!("{} role {}", data.window_id, buf.as_bstr()); + *data.info.role.borrow_mut() = Some(buf.into()); + } + + async fn load_window_wm_class(&self, data: &Rc) { + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, ATOM_WM_CLASS, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => {} + Ok(ty) => { + log::error!("WM_CLASS property has unexpected type {}", ty); + return; + } + Err(XconError::PropertyUnavailable) => { + data.info.instance.borrow_mut().take(); + data.info.class.borrow_mut().take(); + return; + } + Err(e) => { + log::error!("Could not retrieve WM_CLASS property: {}", ErrorFmt(e)); + return; + } + } + let mut iter = buf.split(|c| *c == 0); + *data.info.instance.borrow_mut() = Some(iter.next().unwrap_or(&[]).to_vec().into()); + *data.info.class.borrow_mut() = Some(iter.next().unwrap_or(&[]).to_vec().into()); + } + + async fn load_window_wm_name2(&self, data: &Rc, prop: u32, name: &str) { + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, prop, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING && data.info.utf8_title.get() => return, + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => { + data.info.utf8_title.set(true); + } + Ok(ty) => { + log::error!("{} property has unexpected type {}", name, ty); + return; + } + Err(XconError::PropertyUnavailable) => return, + Err(e) => { + log::error!("Could not retrieve {} property: {}", name, ErrorFmt(e)); + return; + } + } + *data.info.title.borrow_mut() = Some(buf.into()); + } + + async fn load_window_wm_name(&self, data: &Rc) { + self.load_window_wm_name2(data, ATOM_WM_NAME, "WM_NAME") + .await; + } + + async fn load_window_net_wm_name(&self, data: &Rc) { + self.load_window_wm_name2(data, self.atoms._NET_WM_NAME, "_NET_WM_NAME") + .await; + } + + async fn load_window_wm_transient_for(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self.c.get_property::(data.window_id, ATOM_WM_TRANSIENT_FOR, ATOM_WINDOW, &mut buf).await { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve WM_TRANSIENT_FOR property: {}", ErrorFmt(e)); + } + } + if let Some(old) = data.parent.take() { + old.children.remove(&data.window_id); + } + if let Some(w) = buf.first() { + if let Some(w) = self.windows.get(w) { + if data.is_ancestor_of(w.clone()) { + log::error!("Cannot set WM_TRANSIENT_FOR because it would create a cycle"); + return; + } + w.children.set(data.window_id, data.clone()); + data.parent.set(Some(w.clone())); + } + } + } + + async fn load_window_wm_protocols(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms.WM_PROTOCOLS, ATOM_ATOM, &mut buf) + .await + { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve WM_PROTOCOLS property: {}", ErrorFmt(e)); + } + return; + } + data.info.protocols.clear(); + data.info + .protocols + .lock() + .extend(buf.iter().copied().map(|v| (v, ()))); + self.compute_input_model(data); + } + + async fn load_window_wm_hints(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms.WM_HINTS, 0, &mut buf) + .await + { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve WM_HINTS property: {}", ErrorFmt(e)); + } + return; + } + let mut values = [0; 9]; + let len = values.len().min(buf.len()); + values[..len].copy_from_slice(&buf[..len]); + data.info.icccm_hints.flags.set(values[0] as i32); + data.info.icccm_hints.input.set(values[1]); + data.info.icccm_hints.initial_state.set(values[2] as i32); + data.info.icccm_hints.icon_pixmap.set(values[3]); + data.info.icccm_hints.icon_window.set(values[4]); + data.info.icccm_hints.icon_x.set(values[5] as i32); + data.info.icccm_hints.icon_y.set(values[6] as i32); + data.info.icccm_hints.icon_mask.set(values[7]); + data.info.icccm_hints.window_group.set(values[8]); + if data + .info + .icccm_hints + .flags + .get() + .not_contains(ICCCM_WM_HINT_INPUT) + { + data.info.icccm_hints.input.set(1); + } + self.compute_input_model(&data); + } + + async fn load_window_wm_normal_hints(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::( + data.window_id, + self.atoms.WM_NORMAL_HINTS, + ATOM_WM_SIZE_HINTS, + &mut buf, + ) + .await + { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve WM_NORMAL_HINTS property: {}", + ErrorFmt(e) + ); + } + return; + } + let mut values = [0; 18]; + let len = values.len().min(buf.len()); + values[..len].copy_from_slice(&buf[..len]); + data.info.normal_hints.flags.set(values[0]); + data.info.normal_hints.x.set(values[1] as i32); + data.info.normal_hints.y.set(values[2] as i32); + data.info.normal_hints.width.set(values[3] as i32); + data.info.normal_hints.height.set(values[4] as i32); + data.info.normal_hints.min_width.set(values[5] as i32); + data.info.normal_hints.min_height.set(values[6] as i32); + data.info.normal_hints.max_width.set(values[7] as i32); + data.info.normal_hints.max_height.set(values[8] as i32); + data.info.normal_hints.width_inc.set(values[9] as i32); + data.info.normal_hints.height_inc.set(values[10] as i32); + data.info.normal_hints.min_aspect_num.set(values[11] as i32); + data.info.normal_hints.min_aspect_den.set(values[12] as i32); + data.info.normal_hints.max_aspect_num.set(values[13] as i32); + data.info.normal_hints.max_aspect_den.set(values[14] as i32); + data.info.normal_hints.base_width.set(values[15] as i32); + data.info.normal_hints.base_height.set(values[16] as i32); + data.info.normal_hints.win_gravity.set(values[17]); + self.update_wants_floating(data); + } + + async fn load_window_motif_wm_hints(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms._MOTIF_WM_HINTS, 0, &mut buf) + .await + { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve _MOTIF_WM_HINTS property: {}", + ErrorFmt(e) + ); + } + return; + } + let mut values = [0; 5]; + let len = values.len().min(buf.len()); + values[..len].copy_from_slice(&buf[..len]); + data.info + .motif_hints + .flags + .set(values[MWM_HINTS_FLAGS_FIELD]); + data.info + .motif_hints + .decorations + .set(values[MWM_HINTS_DECORATIONS_FIELD]); + } + + async fn load_window_net_startup_id(&self, data: &Rc) { + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, self.atoms._NET_STARTUP_ID, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => {} + Ok(ty) => { + log::error!("_NET_STARTUP_ID property has unexpected type {}", ty); + return; + } + Err(XconError::PropertyUnavailable) => return, + Err(e) => { + log::error!( + "Could not retrieve _NET_STARTUP_ID property: {}", + ErrorFmt(e) + ); + return; + } + } + *data.info.startup_id.borrow_mut() = Some(buf.into()); + } + + async fn load_window_net_wm_state(&self, data: &Rc) { + data.info.fullscreen.set(false); + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms._NET_WM_STATE, 0, &mut buf) + .await + { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve _NET_WM_STATE property: {}", ErrorFmt(e)); + } + return; + } + for prop in buf { + if prop == self.atoms._NET_WM_STATE_MODAL { + data.info.modal.set(true); + self.update_wants_floating(data); + } else if prop == self.atoms._NET_WM_STATE_FULLSCREEN { + data.info.fullscreen.set(true); + } else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_VERT { + data.info.maximized_vert.set(true); + } else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ { + data.info.maximized_horz.set(true); + } else if prop == self.atoms._NET_WM_STATE_HIDDEN { + data.info.minimized.set(true); + } + } + } + + async fn load_window_net_wm_window_type(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::( + data.window_id, + self.atoms._NET_WM_WINDOW_TYPE, + ATOM_ATOM, + &mut buf, + ) + .await + { + if !matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve _NET_WM_WINDOW_TYPE property: {}", + ErrorFmt(e) + ); + } + return; + } + data.info + .never_focus + .set(buf.iter().any(|t| self.never_focus.contains(&t))); + data.info.window_types.clear(); + data.info + .window_types + .lock() + .extend(buf.iter().copied().map(|v| (v, ()))); + self.update_wants_floating(data); + } + + async fn create_window(&mut self, data: &Rc, surface: Rc) { if data.window.get().is_some() { log::error!("The xwindow has already been constructed"); return; @@ -348,17 +833,48 @@ impl Wm { return; } data.window.set(Some(window.clone())); - if surface.buffer.get().is_some() { - self.state.map_tiled(window); + { + self.load_window_wm_class(data).await; + self.load_window_wm_name(data).await; + self.load_window_wm_transient_for(data).await; + self.load_window_wm_protocols(data).await; + self.load_window_wm_hints(data).await; + self.load_window_wm_normal_hints(data).await; + self.load_window_motif_wm_hints(data).await; + self.load_window_net_startup_id(data).await; + self.load_window_net_wm_state(data).await; + self.load_window_net_wm_window_type(data).await; + self.load_window_net_wm_name(data).await; + self.load_window_wm_window_role(data).await; } + { + let specs = [ResClientIdSpec { + client: data.window_id, + mask: RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, + }]; + let c = ResQueryClientIds { + specs: Cow::Borrowed(&specs), + }; + if let Ok(res) = self.c.call(&c).await { + for id in res.get().ids.iter() { + if id.spec.mask.contains(RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + if let Some(first) = id.value.first() { + data.info.pid.set(Some(*first)); + break; + } + } + } + } + } + window.map_status_changed(); } - fn handle_xwayland_surface_created(&mut self, surface: Rc) { + async fn handle_xwayland_surface_created(&mut self, surface: Rc) { let data = match self.windows_by_surface_id.get(&surface.id) { Some(w) => w.clone(), _ => return, }; - self.create_window(&data, surface); + self.create_window(&data, surface).await; } fn handle_xwayland_surface_destroyed(&mut self, surface: WlSurfaceId) { @@ -375,11 +891,15 @@ impl Wm { async fn handle_core_event(&mut self, event: &Event) { let res = match event.code() { MapRequest::OPCODE => self.handle_map_request(event).await, + MapNotify::OPCODE => self.handle_map_notify(event).await, ConfigureRequest::OPCODE => self.handle_configure_request(event).await, ConfigureNotify::OPCODE => self.handle_configure_notify(event), - ClientMessage::OPCODE => self.handle_client_message(event), - CreateNotify::OPCODE => self.handle_create_notify(event), - DestroyNotify::OPCODE => self.handle_destroy_notify(event), + ClientMessage::OPCODE => self.handle_client_message(event).await, + CreateNotify::OPCODE => self.handle_create_notify(event).await, + DestroyNotify::OPCODE => self.handle_destroy_notify(event).await, + PropertyNotify::OPCODE => self.handle_property_notify(event).await, + FocusIn::OPCODE => self.handle_focus_in(event).await, + UnmapNotify::OPCODE => self.handle_unmap_notify(event).await, _ => Ok(()), }; if let Err(e) = res { @@ -387,45 +907,331 @@ impl Wm { } } - fn handle_destroy_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { + async fn handle_unmap_notify(&mut self, revent: &Event) -> Result<(), XWaylandError> { + let event: UnmapNotify = revent.parse()?; + let data = match self.windows.get(&event.window) { + Some(w) => w, + _ => return Ok(()), + }; + if data.map_link.replace(None).is_some() { + self.num_mapped -= 1; + self.set_net_client_list().await; + } + data.info.mapped.set(false); + if let Some(win) = data.window.get() { + win.map_status_changed(); + } + self.set_wm_state(data, ICCCM_WM_STATE_WITHDRAWN).await; + Ok(()) + } + + async fn handle_focus_in(&mut self, revent: &Event) -> Result<(), XWaylandError> { + let event: FocusIn = revent.parse()?; + if matches!(event.mode, NOTIFY_MODE_GRAB | NOTIFY_MODE_UNGRAB) { + return Ok(()); + } + if matches!(event.detail, NOTIFY_DETAIL_POINTER) { + return Ok(()); + } + let new_window = self.windows.get(&event.event); + let mut focus_window = self.focus_window.as_ref(); + if let Some(window) = new_window { + if let Some(prev) = focus_window { + let prev_pid = prev.info.pid.get(); + let new_pid = window.info.pid.get(); + if prev_pid.is_some() && prev_pid == new_pid && revent.serial() >= self.last_input_serial { + focus_window = new_window; + } + } + } + let fw = focus_window.cloned(); + self.focus_window(fw.as_ref()).await; + Ok(()) + } + + async fn close_window(&mut self, window: &Rc) { + if window.info.protocols.contains(&self.atoms.WM_DELETE_WINDOW) { + self.send_wm_message(window, 0, &[self.atoms.WM_DELETE_WINDOW]).await; + } else { + self.c.call(&KillClient { resource: window.window_id }); + } + } + + async fn activate_window(&mut self, window: Option<&Rc>) { + if self.focus_window.as_ref().map(|w| w.window_id) == window.map(|w| w.window_id) { + return; + } + if let Some(w) = window { + if w.destroyed.get() && w.info.override_redirect.get() { + return; + } + if w.info.minimized.get() { + self.set_minimized(w, false).await; + } + } + self.set_net_active_window(window).await; + self.focus_window(window).await; + if let Some(w) = window { + self.stack_window(w, None, true).await; + } + } + + async fn set_net_active_window(&mut self, window: Option<&Rc>) { + let id = window.map(|w| w.window_id).unwrap_or(0); + let cp = ChangeProperty { + mode: PROP_MODE_REPLACE, + window: self.root, + property: self.atoms._NET_ACTIVE_WINDOW, + ty: self.atoms.WINDOW, + format: 32, + data: uapi::as_bytes(&id), + }; + if let Err(e) = self.c.call(&cp).await { + log::error!("Could not set active window: {}", ErrorFmt(e)); + } + } + + async fn handle_property_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { + let event: PropertyNotify = event.parse()?; + let data = match self.windows.get(&event.window) { + Some(w) => w, + _ => return Ok(()), + }; + if event.atom == ATOM_WM_CLASS { + // log::debug!("ATOM_WM_CLASS changed"); + self.load_window_wm_class(data).await; + } else if event.atom == ATOM_WM_NAME { + // log::debug!("ATOM_WM_NAME changed"); + self.load_window_wm_name(data).await; + } else if event.atom == ATOM_WM_TRANSIENT_FOR { + // log::debug!("ATOM_WM_TRANSIENT_FOR changed"); + self.load_window_wm_transient_for(data).await; + } else if event.atom == self.atoms.WM_PROTOCOLS { + // log::debug!("WM_PROTOCOLS changed"); + self.load_window_wm_protocols(data).await; + } else if event.atom == self.atoms.WM_HINTS { + // log::debug!("WM_HINTS changed"); + self.load_window_wm_hints(data).await; + } else if event.atom == self.atoms.WM_NORMAL_HINTS { + // log::debug!("WM_NORMAL_HINTS changed"); + self.load_window_wm_normal_hints(data).await; + } else if event.atom == self.atoms._MOTIF_WM_HINTS { + // log::debug!("_MOTIF_WM_HINTS changed"); + self.load_window_motif_wm_hints(data).await; + } else if event.atom == self.atoms._NET_STARTUP_ID { + // log::debug!("_NET_STARTUP_ID changed"); + self.load_window_net_startup_id(data).await; + } else if event.atom == self.atoms._NET_WM_STATE { + // log::debug!("_NET_WM_STATE changed"); + self.load_window_net_wm_state(data).await; + } else if event.atom == self.atoms._NET_WM_WINDOW_TYPE { + // log::debug!("_NET_WM_WINDOW_TYPE changed"); + self.load_window_net_wm_window_type(data).await; + } else if event.atom == self.atoms._NET_WM_NAME { + // log::debug!("_NET_WM_NAME changed"); + self.load_window_net_wm_name(data).await; + } else if event.atom == self.atoms.WM_WINDOW_ROLE { + // log::debug!("WM_WINDOW_ROLE changed"); + self.load_window_wm_window_role(data).await; + } + Ok(()) + } + + async fn handle_destroy_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { let event: DestroyNotify = event.parse()?; let data = match self.windows.remove(&event.window) { Some(w) => w, _ => return Ok(()), }; + data.destroyed.set(true); if let Some(sid) = data.surface_id.take() { self.windows_by_surface_id.remove(&sid); } if let Some(window) = data.window.take() { window.destroy(); } + if let Some(parent) = data.parent.take() { + parent.children.remove(&data.window_id); + } + { + let mut children = data.children.lock(); + for (_, child) in children.drain() { + child.parent.set(None); + } + } + self.activate_window(None).await; Ok(()) } - fn handle_create_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { + async fn handle_create_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { let event: CreateNotify = event.parse()?; + if event.window == self.xwin { + return Ok(()); + } let data = Rc::new(XwindowData::new(&self.state, &event, &self.client)); + let cwa = ChangeWindowAttributes { + window: event.window, + values: CreateWindowValues { + event_mask: Some(EVENT_MASK_PROPERTY_CHANGE | EVENT_MASK_FOCUS_CHANGE), + ..Default::default() + }, + }; + if let Err(e) = self.c.call(&cwa).await { + log::error!( + "Could not subscribe to events of new window: {}", + ErrorFmt(e) + ); + } + if let Ok(res) = self + .c + .call(&GetGeometry { + drawable: event.window, + }) + .await + { + data.info.has_alpha.set(res.get().depth == 32); + } self.windows.insert(event.window, data); Ok(()) } - fn handle_client_message(&mut self, event: &Event) -> Result<(), XWaylandError> { + async fn handle_client_message(&mut self, event: &Event) -> Result<(), XWaylandError> { let event: ClientMessage = event.parse()?; if event.ty == self.atoms.WL_SURFACE_ID { - self.handle_wl_surface_id(&event)?; + self.handle_wl_surface_id(&event).await?; + } else if event.ty == self.atoms._NET_WM_STATE { + self.handle_net_wm_state(&event).await?; + } else if event.ty == self.atoms._NET_ACTIVE_WINDOW { + self.handle_net_active_window(&event).await?; + } else if event.ty == self.atoms._NET_STARTUP_INFO || event.ty == self.atoms._NET_STARTUP_INFO_BEGIN { + self.handle_net_startup_info(&event).await?; + } else if event.ty == self.atoms.WM_CHANGE_STATE { + self.handle_wm_change_state(&event).await?; + } else if event.ty == self.atoms._NET_WM_MOVERESIZE { + self.handle_net_wm_moveresize(&event).await?; + } + Ok(()) + } + + async fn set_net_client_list_stacking(&mut self) { + let mut windows = Vec::with_capacity(self.num_stacked); + for w in self.stack_list.iter() { + windows.push(w.window_id); + } + let cp = ChangeProperty { + mode: PROP_MODE_REPLACE, + window: self.root, + property: self.atoms._NET_CLIENT_LIST_STACKING, + ty: ATOM_WINDOW, + format: 32, + data: uapi::as_bytes(&windows[..]), + }; + if let Err(e) = self.c.call(&cp).await { + log::error!("Could not set _NET_CLIENT_LIST_STACKING: {}", ErrorFmt(e)); + } + } + + async fn set_net_client_list(&self) { + let mut windows = Vec::with_capacity(self.num_mapped); + for w in self.map_list.iter() { + windows.push(w.window_id); + } + let cp = ChangeProperty { + mode: PROP_MODE_REPLACE, + window: self.root, + property: self.atoms._NET_CLIENT_LIST, + ty: ATOM_WINDOW, + format: 32, + data: uapi::as_bytes(&windows[..]), + }; + if let Err(e) = self.c.call(&cp).await { + log::error!("Could not set _NET_CLIENT_LIST: {}", ErrorFmt(e)); + } + } + + fn update_override_redirect(&self, data: &Rc, or: u8) { + let or = or != 0; + if data.info.override_redirect.replace(or) != or { + if let Some(window) = data.window.get() { + window.destroy_node(true); + window.map_status_changed(); + } + } + } + + async fn handle_map_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { + let event: MapNotify = event.parse()?; + let data = match self.windows.get(&event.window) { + Some(d) => d, + _ => return Ok(()), + }; + self.update_override_redirect(data, event.override_redirect); + data.info.mapped.set(true); + if let Some(win) = data.window.get() { + win.map_status_changed(); } Ok(()) } async fn handle_map_request(&mut self, event: &Event) -> Result<(), XWaylandError> { let event: MapRequest = event.parse()?; + let data = match self.windows.get(&event.window) { + Some(w) => w.clone(), + _ => return Ok(()), + }; + self.set_wm_state(&data, ICCCM_WM_STATE_NORMAL).await; + self.set_net_wm_state(&data).await; + if data.map_link.replace(Some(self.map_list.add_last(data.clone()))).is_none() { + self.num_mapped += 1; + } + self.set_net_client_list().await; + self.stack_window(&data, None, true).await; let mw = MapWindow { window: event.window, }; - match self.c.call(&mw).await { - Ok(_) => Ok(()), - Err(e) => Err(XWaylandError::MapWindow(e)), + if let Err(e) = self.c.call(&mw).await { + log::error!("Could not map window: {}", ErrorFmt(e)); } + Ok(()) + } + + async fn stack_window(&mut self, window: &Rc, sibling: Option<&Rc>, above: bool) { + let link = 'link: { + if let Some(s) = sibling { + if s.window_id == window.window_id { + log::warn!("trying to stack window above itself"); + } else { + let sl = s.stack_link.borrow_mut(); + if let Some(sl) = sl.deref() { + break 'link if above { + sl.append(window.clone()) + } else { + sl.prepend(window.clone()) + }; + } + } + } + if above { + self.stack_list.add_last(window.clone()) + } else { + self.stack_list.add_first(window.clone()) + } + }; + *window.stack_link.borrow_mut() = Some(link); + self.set_net_client_list_stacking().await; + } + + async fn set_wm_state(&self, data: &Rc, state: u32) { + let property = [state, 0]; + let cp = ChangeProperty { + mode: PROP_MODE_REPLACE, + window: data.window_id, + property: self.atoms.WM_STATE, + ty: self.atoms.WM_STATE, + format: 32, + data: uapi::as_bytes(&property[..]), + }; + self.c.call(&cp); } fn handle_configure_notify(&mut self, event: &Event) -> Result<(), XWaylandError> { @@ -434,15 +1240,16 @@ impl Wm { Some(d) => d, _ => return Ok(()), }; - if data.override_redirect { + self.update_override_redirect(data, event.override_redirect); + if data.info.override_redirect.get() { let extents = Rect::new_sized( event.x as _, event.y as _, event.width as _, event.height as _, ) - .unwrap(); - let changed = data.extents.replace(extents) != extents; + .unwrap(); + let changed = data.info.extents.replace(extents) != extents; if changed { self.state.tree_changed(); } @@ -462,7 +1269,130 @@ impl Wm { Ok(()) } - fn handle_wl_surface_id(&mut self, event: &ClientMessage) -> Result<(), XWaylandError> { + async fn handle_net_wm_moveresize( + &mut self, + event: &ClientMessage<'_>, + ) -> Result<(), XWaylandError> { + let _data = match self.windows.get(&event.window) { + Some(d) => d, + _ => return Ok(()), + }; + let _detail = event.data[2]; + Ok(()) + } + + async fn handle_wm_change_state( + &mut self, + event: &ClientMessage<'_>, + ) -> Result<(), XWaylandError> { + let _data = match self.windows.get(&event.window) { + Some(d) => d, + _ => return Ok(()), + }; + let _minimize = match event.data[0] { + ICCCM_WM_STATE_NORMAL => false, + ICCCM_WM_STATE_ICONIC => true, + _ => return Ok(()), + }; + Ok(()) + } + + async fn handle_net_startup_info( + &mut self, + event: &ClientMessage<'_>, + ) -> Result<(), XWaylandError> { + let data = match self.windows.get(&event.window) { + Some(d) => d, + _ => return Ok(()), + }; + let mut startup_info = data.startup_info.borrow_mut(); + let mut msg = uapi::as_bytes(event.data); + let mut end = false; + if let Some(pos) = msg.find_byte(0) { + end = true; + msg = &msg[..pos]; + } + startup_info.extend_from_slice(msg); + if !end { + return Ok(()); + } + if let Some(id) = startup_info.strip_prefix(b"remove: ID=") { + log::info!("Got startup id {}", id.as_bstr()); + } else { + log::warn!("Unhandled startup info: {}", startup_info.as_bstr()); + } + mem::take(startup_info.deref_mut()); + Ok(()) + } + + async fn handle_net_active_window( + &mut self, + event: &ClientMessage<'_>, + ) -> Result<(), XWaylandError> { + let _data = match self.windows.get(&event.window) { + Some(d) => d, + _ => return Ok(()), + }; + // TODO activate + Ok(()) + } + + async fn handle_net_wm_state( + &mut self, + event: &ClientMessage<'_>, + ) -> Result<(), XWaylandError> { + let data = match self.windows.get(&event.window) { + Some(d) => d, + _ => return Ok(()), + }; + let mut changed = false; + let mut fullscreen = data.info.fullscreen.get(); + let mut maximized_horz = data.info.maximized_horz.get(); + let mut maximized_vert = data.info.maximized_vert.get(); + let mut minimized = data.info.minimized.get(); + let mut modal = data.info.modal.get(); + let action = event.data[0]; + let mut update = |prop: &mut bool| { + let new = match action { + _NET_WM_STATE_REMOVE => false, + _NET_WM_STATE_ADD => true, + _NET_WM_STATE_TOGGLE => !*prop, + _ => return, + }; + if mem::replace(prop, new) != new { + changed = true; + } + }; + for p in [event.data[1], event.data[2]] { + if p == self.atoms._NET_WM_STATE_MODAL { + update(&mut modal); + } else if p == self.atoms._NET_WM_STATE_FULLSCREEN { + update(&mut fullscreen); + } else if p == self.atoms._NET_WM_STATE_MAXIMIZED_VERT { + update(&mut maximized_vert); + } else if p == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ { + update(&mut maximized_horz); + } else if p == self.atoms._NET_WM_STATE_HIDDEN { + update(&mut minimized); + } + } + if !changed { + return Ok(()); + } + data.info.fullscreen.set(fullscreen); + data.info.maximized_horz.set(maximized_horz); + data.info.maximized_vert.set(maximized_vert); + data.info.minimized.set(minimized); + data.info.modal.set(modal); + self.update_wants_floating(data); + self.set_net_wm_state(data).await; + Ok(()) + } + + async fn handle_wl_surface_id( + &mut self, + event: &ClientMessage<'_>, + ) -> Result<(), XWaylandError> { let data = match self.windows.get(&event.window) { Some(d) => d.clone(), _ => return Ok(()), @@ -476,8 +1406,25 @@ impl Wm { data.surface_id.set(Some(surface_id)); self.windows_by_surface_id.insert(surface_id, data.clone()); if let Ok(surface) = self.client.lookup(surface_id) { - self.create_window(&data, surface); + self.create_window(&data, surface).await; } Ok(()) } + + fn update_wants_floating(&self, data: &Rc) { + let res = false + || data.info.modal.get() + || data.info.window_types.contains(&self.atoms._NET_WM_WINDOW_TYPE_DIALOG) + || data.info.window_types.contains(&self.atoms._NET_WM_WINDOW_TYPE_UTILITY) + || data.info.window_types.contains(&self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR) + || data.info.window_types.contains(&self.atoms._NET_WM_WINDOW_TYPE_SPLASH) + || { + let max_w = data.info.normal_hints.max_width.get(); + let min_w = data.info.normal_hints.min_width.get(); + let max_h = data.info.normal_hints.max_height.get(); + let min_h = data.info.normal_hints.min_height.get(); + max_w > 0 && max_h > 0 && max_w == min_w && max_h == min_h + }; + data.info.wants_floating.set(res); + } } diff --git a/wire-xcon/res.txt b/wire-xcon/res.txt new file mode 100644 index 00000000..d0bca299 --- /dev/null +++ b/wire-xcon/res.txt @@ -0,0 +1,22 @@ +ext "X-Resource" + +struct ResClientIdSpec { + client: u32, + mask: u32, +} + +struct ResClientIdValue { + spec: ResClientIdSpec, + length: u32 = mul(len(value), literal(4)), + value: list(u32, div(field(length), literal(4))), +} + +request ResQueryClientIds = 4 ( + num_specs: u32 = len(specs), + specs: list(ResClientIdSpec, field(num_specs)), +) { + @pad 1, + num_ids: u32 = len(ids), + @pad 20, + ids: list(ResClientIdValue, field(num_ids)), +} diff --git a/wire-xcon/xproto.txt b/wire-xcon/xproto.txt index 72d0bf10..c00cf3dd 100644 --- a/wire-xcon/xproto.txt +++ b/wire-xcon/xproto.txt @@ -244,6 +244,22 @@ request ChangeProperty = 18 ( data: list(u8, mul(field(data_len), div(field(format), literal(8)))), ); +request GetProperty = 20 ( + delete: u8, + window: u32, + property: u32, + ty: u32, + long_offset: u32, + long_length: u32, +) { + format: u8, + ty: u32, + bytes_after: u32, + value_len: u32 = div(mul(len(data), literal(8)), field(format)), + @pad 12, + data: list(u8, mul(field(value_len), div(field(format), literal(8)))), +} + request InternAtom = 16 ( only_if_exists: u8, name_len: u16 = len(name), @@ -338,3 +354,70 @@ event ClientMessage = 33 { ty: u32, data: list(u32, literal(5)), } + +request GetGeometry = 14 ( + @pad 1, + drawable: u32, +) { + depth: u8, + root: u32, + x: i16, + y: i16, + width: u16, + height: u16, + border_width: u16, + @pad 2, +} + +event PropertyNotify = 28 { + @pad 1, + window: u32, + atom: u32, + time: u32, + state: u8, + @pad 3, +} + +request GetAtomName = 17 ( + @pad 1, + atom: u32, +) { + @pad 1, + name_len: u16 = len(name), + @pad 22, + name: str(field(name_len)), +} + +request SetInputFocus = 42 ( + revert_to: u8, + focus: u32, + time: u32, +); + +event FocusIn = 9 { + detail: u8, + event: u32, + mode: u8, + @pad 3, +} + +request KillClient = 113 ( + @pad 1, + resource: u32, +); + +event UnmapNotify = 18 { + @pad 1, + event: u32, + window: u32, + from_configure: u8, + @pad 8, +} + +event MapNotify = 19 { + @pad 1, + event: u32, + window: u32, + override_redirect: u8, + @pad 3, +}