wip: New shell logic
This commit is contained in:
parent
146a4893ca
commit
00f1b029da
39 changed files with 3922 additions and 2503 deletions
631
src/shell/element/mod.rs
Normal file
631
src/shell/element/mod.rs
Normal file
|
|
@ -0,0 +1,631 @@
|
|||
use crate::state::State;
|
||||
use id_tree::NodeId;
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::KeyState,
|
||||
renderer::{element::AsRenderElements, ImportAll, Renderer},
|
||||
},
|
||||
desktop::{space::SpaceElement, Kind, PopupManager, Window, WindowSurfaceType},
|
||||
input::{
|
||||
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState,
|
||||
wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface},
|
||||
},
|
||||
render_elements, space_elements,
|
||||
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
|
||||
wayland::{
|
||||
compositor::{with_states, with_surface_tree_downward, TraversalAction},
|
||||
seat::WaylandFocus,
|
||||
shell::xdg::XdgToplevelSurfaceRoleAttributes,
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
pub mod stack;
|
||||
pub use self::stack::CosmicStack;
|
||||
pub mod window;
|
||||
pub use self::window::CosmicWindow;
|
||||
|
||||
use super::focus::FocusDirection;
|
||||
|
||||
space_elements! {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
CosmicMappedInternal;
|
||||
Window=CosmicWindow,
|
||||
Stack=CosmicStack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CosmicMapped {
|
||||
element: CosmicMappedInternal,
|
||||
|
||||
// associated data
|
||||
|
||||
//tiling
|
||||
pub(super) tiling_node_id: Arc<Mutex<Option<NodeId>>>,
|
||||
//floating
|
||||
pub(super) last_geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>,
|
||||
}
|
||||
|
||||
impl PartialEq for CosmicMapped {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.element == other.element
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CosmicMapped {}
|
||||
|
||||
impl Hash for CosmicMapped {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.element.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl CosmicMapped {
|
||||
pub fn windows(&self) -> impl Iterator<Item = (Window, Point<i32, Logical>)> + '_ {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(stack) => Box::new(stack.windows().map(|w| {
|
||||
(
|
||||
w,
|
||||
stack
|
||||
.header
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|header| Point::from((0, header.height() as i32)))
|
||||
.unwrap_or(Point::from((0, 0))),
|
||||
)
|
||||
}))
|
||||
as Box<dyn Iterator<Item = (Window, Point<i32, Logical>)>>,
|
||||
CosmicMappedInternal::Window(window) => Box::new(std::iter::once((
|
||||
window.window.clone(),
|
||||
window
|
||||
.header
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|header| Point::from((0, header.height() as i32)))
|
||||
.unwrap_or(Point::from((0, 0))),
|
||||
))),
|
||||
_ => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_window(&self) -> Window {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(stack) => stack.active(),
|
||||
CosmicMappedInternal::Window(win) => win.window.clone(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus_window(&self, window: &Window) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(stack) => stack.set_active(window),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_surface(&self, surface: &WlSurface, surface_type: WindowSurfaceType) -> bool {
|
||||
self.windows().any(|(w, _)| {
|
||||
let toplevel = w.toplevel().wl_surface();
|
||||
|
||||
if surface_type.contains(WindowSurfaceType::TOPLEVEL) {
|
||||
if toplevel == surface {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if surface_type.contains(WindowSurfaceType::SUBSURFACE) {
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
let found = AtomicBool::new(false);
|
||||
with_surface_tree_downward(
|
||||
toplevel,
|
||||
surface,
|
||||
|_, _, search| TraversalAction::DoChildren(search),
|
||||
|s, _, search| {
|
||||
found.fetch_or(s == *search, Ordering::SeqCst);
|
||||
},
|
||||
|_, _, _| !found.load(Ordering::SeqCst),
|
||||
);
|
||||
if found.load(Ordering::SeqCst) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if surface_type.contains(WindowSurfaceType::POPUP) {
|
||||
PopupManager::popups_for_surface(toplevel).any(|(p, _)| p.wl_surface() == surface)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_focus(&self, direction: FocusDirection) -> bool {
|
||||
if let CosmicMappedInternal::Stack(stack) = &self.element {
|
||||
//TODO: stack.handle_focus(direction)
|
||||
false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_tiled(&self, tiled: bool) {
|
||||
for toplevel in match &self.element {
|
||||
// we use the tiled state of stack windows anyway to get rid of decorations
|
||||
CosmicMappedInternal::Stack(s) => None,
|
||||
CosmicMappedInternal::Window(w) => Some(w.window.toplevel()),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
match toplevel {
|
||||
Kind::Xdg(xdg) => xdg.with_pending_state(|state| {
|
||||
if tiled {
|
||||
state.states.set(XdgState::TiledLeft);
|
||||
state.states.set(XdgState::TiledRight);
|
||||
state.states.set(XdgState::TiledTop);
|
||||
state.states.set(XdgState::TiledBottom);
|
||||
} else {
|
||||
state.states.unset(XdgState::TiledLeft);
|
||||
state.states.unset(XdgState::TiledRight);
|
||||
state.states.unset(XdgState::TiledTop);
|
||||
state.states.unset(XdgState::TiledBottom);
|
||||
}
|
||||
}),
|
||||
// Kind::X11?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tiled(&self) -> bool {
|
||||
let window = match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.active(),
|
||||
CosmicMappedInternal::Window(w) => w.window.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::TiledLeft),
|
||||
// Kind::X11?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&self, fullscreen: bool) {
|
||||
for window in match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => {
|
||||
Box::new(s.windows()) as Box<dyn Iterator<Item = Window>>
|
||||
}
|
||||
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.with_pending_state(|state| {
|
||||
if fullscreen {
|
||||
state.states.set(XdgState::Fullscreen);
|
||||
} else {
|
||||
state.states.unset(XdgState::Fullscreen);
|
||||
}
|
||||
}),
|
||||
// Kind::X11?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
let window = match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.active(),
|
||||
CosmicMappedInternal::Window(w) => w.window.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Fullscreen),
|
||||
// Kind::X11?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
for window in match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => {
|
||||
Box::new(s.windows()) as Box<dyn Iterator<Item = Window>>
|
||||
}
|
||||
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.with_pending_state(|state| {
|
||||
if maximized {
|
||||
state.states.set(XdgState::Maximized);
|
||||
} else {
|
||||
state.states.unset(XdgState::Maximized);
|
||||
}
|
||||
}),
|
||||
// Kind::X11?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
let window = match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.active(),
|
||||
CosmicMappedInternal::Window(w) => w.window.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Maximized),
|
||||
// Kind::X11?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_activated(&self, activated: bool) {
|
||||
for window in match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => {
|
||||
Box::new(s.windows()) as Box<dyn Iterator<Item = Window>>
|
||||
}
|
||||
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.with_pending_state(|state| {
|
||||
if activated {
|
||||
state.states.set(XdgState::Activated);
|
||||
} else {
|
||||
state.states.unset(XdgState::Activated);
|
||||
}
|
||||
}),
|
||||
// Kind::X11?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_activated(&self) -> bool {
|
||||
let window = match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.active(),
|
||||
CosmicMappedInternal::Window(w) => w.window.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Activated),
|
||||
// Kind::X11?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_size(&self, size: Size<i32, Logical>) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.set_size(size),
|
||||
CosmicMappedInternal::Window(w) => w.set_size(size),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_size(&self) -> Size<i32, Logical> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(stack) => stack
|
||||
.windows()
|
||||
.fold(None, |min_size, window| {
|
||||
let win_min_size = with_states(window.toplevel().wl_surface(), |states| {
|
||||
let attrs = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
attrs.min_size
|
||||
});
|
||||
match (min_size, win_min_size) {
|
||||
(None, x) => Some(x),
|
||||
(Some(min1), min2) => Some((min1.w.max(min2.w), min1.h.max(min2.h)).into()),
|
||||
}
|
||||
})
|
||||
.expect("Empty stack?"),
|
||||
CosmicMappedInternal::Window(window) => {
|
||||
with_states(window.window.toplevel().wl_surface(), |states| {
|
||||
let attrs = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
attrs.min_size
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_size(&self) -> Size<i32, Logical> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(stack) => {
|
||||
let theoretical_max = stack.windows().fold(None, |max_size, window| {
|
||||
let win_max_size = with_states(window.toplevel().wl_surface(), |states| {
|
||||
let attrs = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
attrs.max_size
|
||||
});
|
||||
match (max_size, win_max_size) {
|
||||
(None, x) => Some(x),
|
||||
(Some(max1), max2) => Some(
|
||||
(
|
||||
if max1.w == 0 {
|
||||
max2.w
|
||||
} else if max2.w == 0 {
|
||||
max1.w
|
||||
} else {
|
||||
max1.w.min(max2.w)
|
||||
},
|
||||
if max1.h == 0 {
|
||||
max2.h
|
||||
} else if max2.h == 0 {
|
||||
max1.h
|
||||
} else {
|
||||
max1.h.min(max2.h)
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
});
|
||||
// The problem is, with accumulated sizes, the minimum size could be larger than our maximum...
|
||||
let min_size = self.min_size();
|
||||
match (theoretical_max, min_size) {
|
||||
(None, _) => (0, 0).into(),
|
||||
(Some(max), min) => (max.w.max(min.w), max.h.max(min.h)).into(),
|
||||
}
|
||||
}
|
||||
CosmicMappedInternal::Window(window) => {
|
||||
with_states(window.window.toplevel().wl_surface(), |states| {
|
||||
let attrs = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
attrs.max_size
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(&self) {
|
||||
for window in match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => {
|
||||
Box::new(s.windows()) as Box<dyn Iterator<Item = Window>>
|
||||
}
|
||||
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.send_configure(),
|
||||
// Kind::X11?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_close(&self) {
|
||||
let window = match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.active(),
|
||||
CosmicMappedInternal::Window(w) => w.window.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.send_close(),
|
||||
// Kind::X11?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for CosmicMapped {
|
||||
fn alive(&self) -> bool {
|
||||
self.element.alive()
|
||||
}
|
||||
}
|
||||
|
||||
impl SpaceElement for CosmicMapped {
|
||||
fn bbox(&self) -> Rectangle<i32, Logical> {
|
||||
SpaceElement::bbox(&self.element)
|
||||
}
|
||||
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
||||
SpaceElement::is_in_input_region(&self.element, point)
|
||||
}
|
||||
fn set_activate(&self, activated: bool) {
|
||||
SpaceElement::set_activate(&self.element, activated)
|
||||
}
|
||||
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
|
||||
SpaceElement::output_enter(&self.element, output, overlap)
|
||||
}
|
||||
fn output_leave(&self, output: &Output) {
|
||||
SpaceElement::output_leave(&self.element, output)
|
||||
}
|
||||
fn geometry(&self) -> Rectangle<i32, Logical> {
|
||||
SpaceElement::geometry(&self.element)
|
||||
}
|
||||
fn z_index(&self) -> u8 {
|
||||
SpaceElement::z_index(&self.element)
|
||||
}
|
||||
fn refresh(&self) {
|
||||
SpaceElement::refresh(&self.element)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardTarget<State> for CosmicMapped {
|
||||
fn enter(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
keys: Vec<KeysymHandle<'_>>,
|
||||
serial: Serial,
|
||||
) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => KeyboardTarget::enter(s, seat, data, keys, serial),
|
||||
CosmicMappedInternal::Window(w) => KeyboardTarget::enter(w, seat, data, keys, serial),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => KeyboardTarget::leave(s, seat, data, serial),
|
||||
CosmicMappedInternal::Window(w) => KeyboardTarget::leave(w, seat, data, serial),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn key(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
key: KeysymHandle<'_>,
|
||||
state: KeyState,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => {
|
||||
KeyboardTarget::key(s, seat, data, key, state, serial, time)
|
||||
}
|
||||
CosmicMappedInternal::Window(w) => {
|
||||
KeyboardTarget::key(w, seat, data, key, state, serial, time)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn modifiers(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
modifiers: ModifiersState,
|
||||
serial: Serial,
|
||||
) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => {
|
||||
KeyboardTarget::modifiers(s, seat, data, modifiers, serial)
|
||||
}
|
||||
CosmicMappedInternal::Window(w) => {
|
||||
KeyboardTarget::modifiers(w, seat, data, modifiers, serial)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerTarget<State> for CosmicMapped {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => PointerTarget::enter(s, seat, data, event),
|
||||
CosmicMappedInternal::Window(w) => PointerTarget::enter(w, seat, data, event),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => PointerTarget::motion(s, seat, data, event),
|
||||
CosmicMappedInternal::Window(w) => PointerTarget::motion(w, seat, data, event),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => PointerTarget::button(s, seat, data, event),
|
||||
CosmicMappedInternal::Window(w) => PointerTarget::button(w, seat, data, event),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => PointerTarget::axis(s, seat, data, frame),
|
||||
CosmicMappedInternal::Window(w) => PointerTarget::axis(w, seat, data, frame),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => PointerTarget::leave(s, seat, data, serial, time),
|
||||
CosmicMappedInternal::Window(w) => PointerTarget::leave(w, seat, data, serial, time),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandFocus for CosmicMapped {
|
||||
fn wl_surface(&self) -> Option<WlSurface> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Window(w) => w.window.wl_surface().clone(),
|
||||
CosmicMappedInternal::Stack(s) => s.active().wl_surface().clone(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn same_client_as(&self, object_id: &ObjectId) -> bool {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Window(w) => w.window.same_client_as(object_id),
|
||||
CosmicMappedInternal::Stack(s) => s.windows().any(|w| w.same_client_as(object_id)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CosmicWindow> for CosmicMapped {
|
||||
fn from(w: CosmicWindow) -> Self {
|
||||
CosmicMapped {
|
||||
element: CosmicMappedInternal::Window(w),
|
||||
tiling_node_id: Arc::new(Mutex::new(None)),
|
||||
last_geometry: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CosmicStack> for CosmicMapped {
|
||||
fn from(s: CosmicStack) -> Self {
|
||||
CosmicMapped {
|
||||
element: CosmicMappedInternal::Stack(s),
|
||||
tiling_node_id: Arc::new(Mutex::new(None)),
|
||||
last_geometry: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render_elements! {
|
||||
pub CosmicMappedRenderElement<R> where R: ImportAll;
|
||||
Stack=self::stack::CosmicStackRenderElement<R>,
|
||||
Window=self::window::CosmicWindowRenderElement<R>,
|
||||
}
|
||||
|
||||
impl<R> AsRenderElements<R> for CosmicMapped
|
||||
where
|
||||
R: Renderer + ImportAll,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
type RenderElement = CosmicMappedRenderElement<R>;
|
||||
fn render_elements<C: From<Self::RenderElement>>(
|
||||
&self,
|
||||
location: Point<i32, Physical>,
|
||||
scale: Scale<f64>,
|
||||
) -> Vec<C> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => AsRenderElements::<R>::render_elements::<
|
||||
CosmicMappedRenderElement<R>,
|
||||
>(s, location, scale),
|
||||
CosmicMappedInternal::Window(w) => AsRenderElements::<R>::render_elements::<
|
||||
CosmicMappedRenderElement<R>,
|
||||
>(w, location, scale),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
.into_iter()
|
||||
.map(C::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
293
src/shell/element/stack.rs
Normal file
293
src/shell/element/stack.rs
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
use crate::state::State;
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::KeyState,
|
||||
renderer::{
|
||||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
|
||||
ImportAll, Renderer,
|
||||
},
|
||||
},
|
||||
desktop::{space::SpaceElement, Kind, Window},
|
||||
input::{
|
||||
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
render_elements,
|
||||
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
|
||||
};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CosmicStack {
|
||||
windows: Arc<Mutex<Vec<Window>>>,
|
||||
active: Arc<AtomicUsize>,
|
||||
pub(super) header: Arc<Mutex<Option<HeaderBar>>>,
|
||||
}
|
||||
|
||||
impl PartialEq for CosmicStack {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.windows.lock().unwrap() == *other.windows.lock().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CosmicStack {}
|
||||
|
||||
impl Hash for CosmicStack {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
Arc::as_ptr(&self.windows).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeaderBar {}
|
||||
|
||||
impl HeaderBar {
|
||||
pub fn height(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl CosmicStack {
|
||||
pub fn active(&self) -> Window {
|
||||
self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)].clone()
|
||||
}
|
||||
|
||||
pub fn set_active(&self, window: &Window) {
|
||||
if let Some(val) = self
|
||||
.windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.position(|w| w == window)
|
||||
{
|
||||
self.active.store(val, Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_size(&self, size: Size<i32, Logical>) {
|
||||
let surface_size = (
|
||||
size.w,
|
||||
size.h
|
||||
- self
|
||||
.header
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|h| h.height())
|
||||
.unwrap_or(0),
|
||||
)
|
||||
.into();
|
||||
|
||||
for window in self.windows.lock().unwrap().iter() {
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.with_pending_state(|state| state.size = Some(surface_size)),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for CosmicStack {
|
||||
fn alive(&self) -> bool {
|
||||
self.windows.lock().unwrap().iter().any(IsAlive::alive)
|
||||
}
|
||||
}
|
||||
|
||||
impl SpaceElement for CosmicStack {
|
||||
fn bbox(&self) -> Rectangle<i32, Logical> {
|
||||
SpaceElement::bbox(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)])
|
||||
}
|
||||
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
||||
SpaceElement::is_in_input_region(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
point,
|
||||
)
|
||||
}
|
||||
fn set_activate(&self, activated: bool) {
|
||||
self.windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.for_each(|w| SpaceElement::set_activate(w, activated))
|
||||
}
|
||||
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
|
||||
self.windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.for_each(|w| SpaceElement::output_enter(w, output, overlap))
|
||||
}
|
||||
fn output_leave(&self, output: &Output) {
|
||||
self.windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.for_each(|w| SpaceElement::output_leave(w, output))
|
||||
}
|
||||
fn geometry(&self) -> Rectangle<i32, Logical> {
|
||||
SpaceElement::geometry(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)])
|
||||
}
|
||||
fn z_index(&self) -> u8 {
|
||||
SpaceElement::z_index(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)])
|
||||
}
|
||||
fn refresh(&self) {
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
windows.retain(IsAlive::alive);
|
||||
let len = windows.len();
|
||||
let _ = self
|
||||
.active
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| {
|
||||
(active > len).then_some(len - 1)
|
||||
});
|
||||
windows.iter().for_each(|w| SpaceElement::refresh(w))
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardTarget<State> for CosmicStack {
|
||||
fn enter(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
keys: Vec<KeysymHandle<'_>>,
|
||||
serial: Serial,
|
||||
) {
|
||||
KeyboardTarget::enter(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
keys,
|
||||
serial,
|
||||
)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
|
||||
KeyboardTarget::leave(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
serial,
|
||||
)
|
||||
}
|
||||
fn key(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
key: KeysymHandle<'_>,
|
||||
state: KeyState,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
KeyboardTarget::key(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
key,
|
||||
state,
|
||||
serial,
|
||||
time,
|
||||
)
|
||||
}
|
||||
fn modifiers(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
modifiers: ModifiersState,
|
||||
serial: Serial,
|
||||
) {
|
||||
KeyboardTarget::modifiers(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
modifiers,
|
||||
serial,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerTarget<State> for CosmicStack {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
PointerTarget::enter(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
event,
|
||||
)
|
||||
}
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
PointerTarget::motion(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
event,
|
||||
)
|
||||
}
|
||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||
PointerTarget::button(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
event,
|
||||
)
|
||||
}
|
||||
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
||||
PointerTarget::axis(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
frame,
|
||||
)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
PointerTarget::leave(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
serial,
|
||||
time,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render_elements! {
|
||||
pub CosmicStackRenderElement<R> where R: ImportAll;
|
||||
Window=WaylandSurfaceRenderElement,
|
||||
}
|
||||
|
||||
impl<R> AsRenderElements<R> for CosmicStack
|
||||
where
|
||||
R: Renderer + ImportAll,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
type RenderElement = CosmicStackRenderElement<R>;
|
||||
fn render_elements<C: From<Self::RenderElement>>(
|
||||
&self,
|
||||
location: Point<i32, Physical>,
|
||||
scale: Scale<f64>,
|
||||
) -> Vec<C> {
|
||||
AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
location,
|
||||
scale,
|
||||
)
|
||||
.into_iter()
|
||||
.map(C::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
impl CosmicStack {
|
||||
pub fn windows(&self) -> impl Iterator<Item = Window> {
|
||||
self.windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
231
src/shell/element/window.rs
Normal file
231
src/shell/element/window.rs
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
use crate::state::State;
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::KeyState,
|
||||
renderer::{
|
||||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
|
||||
ImportAll, Renderer,
|
||||
},
|
||||
},
|
||||
desktop::{space::SpaceElement, Kind, Window},
|
||||
input::{
|
||||
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode,
|
||||
render_elements,
|
||||
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
|
||||
wayland::shell::xdg::ToplevelSurface,
|
||||
};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CosmicWindow {
|
||||
pub(super) window: Window,
|
||||
pub(super) header: Arc<Mutex<Option<HeaderBar>>>,
|
||||
}
|
||||
|
||||
impl PartialEq<Window> for CosmicWindow {
|
||||
fn eq(&self, other: &Window) -> bool {
|
||||
&self.window == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<CosmicWindow> for Window {
|
||||
fn eq(&self, other: &CosmicWindow) -> bool {
|
||||
self == &other.window
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for CosmicWindow {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.window == other.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CosmicWindow {}
|
||||
|
||||
impl Hash for CosmicWindow {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.window.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl CosmicWindow {
|
||||
pub fn set_size(&self, size: Size<i32, Logical>) {
|
||||
let surface_size = (
|
||||
size.w,
|
||||
size.h
|
||||
- self
|
||||
.header
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|h| h.height())
|
||||
.unwrap_or(0),
|
||||
)
|
||||
.into();
|
||||
match self.window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.with_pending_state(|state| state.size = Some(surface_size)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Window> for CosmicWindow {
|
||||
fn from(window: Window) -> Self {
|
||||
let is_ssd = matches!(
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(xdg) => xdg.current_state().decoration_mode,
|
||||
},
|
||||
Some(DecorationMode::ServerSide)
|
||||
);
|
||||
CosmicWindow {
|
||||
window,
|
||||
header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToplevelSurface> for CosmicWindow {
|
||||
fn from(surf: ToplevelSurface) -> Self {
|
||||
let is_ssd = matches!(
|
||||
surf.current_state().decoration_mode,
|
||||
Some(DecorationMode::ServerSide)
|
||||
);
|
||||
CosmicWindow {
|
||||
window: Window::new(Kind::Xdg(surf)),
|
||||
header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub(super) struct HeaderBar {
|
||||
pointer_loc: Option<Point<f64, Logical>>,
|
||||
close_button_hover: bool,
|
||||
maximize_button_hover: bool,
|
||||
}
|
||||
|
||||
impl HeaderBar {
|
||||
pub fn height(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for CosmicWindow {
|
||||
fn alive(&self) -> bool {
|
||||
self.window.alive()
|
||||
}
|
||||
}
|
||||
|
||||
impl SpaceElement for CosmicWindow {
|
||||
fn bbox(&self) -> Rectangle<i32, Logical> {
|
||||
SpaceElement::bbox(&self.window)
|
||||
}
|
||||
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
||||
SpaceElement::is_in_input_region(&self.window, point)
|
||||
}
|
||||
fn set_activate(&self, activated: bool) {
|
||||
SpaceElement::set_activate(&self.window, activated)
|
||||
}
|
||||
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
|
||||
SpaceElement::output_enter(&self.window, output, overlap)
|
||||
}
|
||||
fn output_leave(&self, output: &Output) {
|
||||
SpaceElement::output_leave(&self.window, output)
|
||||
}
|
||||
fn geometry(&self) -> Rectangle<i32, Logical> {
|
||||
SpaceElement::geometry(&self.window)
|
||||
}
|
||||
fn z_index(&self) -> u8 {
|
||||
SpaceElement::z_index(&self.window)
|
||||
}
|
||||
fn refresh(&self) {
|
||||
SpaceElement::refresh(&self.window)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardTarget<State> for CosmicWindow {
|
||||
fn enter(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
keys: Vec<KeysymHandle<'_>>,
|
||||
serial: Serial,
|
||||
) {
|
||||
KeyboardTarget::enter(&self.window, seat, data, keys, serial)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
|
||||
KeyboardTarget::leave(&self.window, seat, data, serial)
|
||||
}
|
||||
fn key(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
key: KeysymHandle<'_>,
|
||||
state: KeyState,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
KeyboardTarget::key(&self.window, seat, data, key, state, serial, time)
|
||||
}
|
||||
fn modifiers(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
modifiers: ModifiersState,
|
||||
serial: Serial,
|
||||
) {
|
||||
KeyboardTarget::modifiers(&self.window, seat, data, modifiers, serial)
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerTarget<State> for CosmicWindow {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
PointerTarget::enter(&self.window, seat, data, event)
|
||||
}
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
PointerTarget::motion(&self.window, seat, data, event)
|
||||
}
|
||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||
PointerTarget::button(&self.window, seat, data, event)
|
||||
}
|
||||
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
||||
PointerTarget::axis(&self.window, seat, data, frame)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
PointerTarget::leave(&self.window, seat, data, serial, time)
|
||||
}
|
||||
}
|
||||
|
||||
render_elements! {
|
||||
pub CosmicWindowRenderElement<R> where R: ImportAll;
|
||||
Window=WaylandSurfaceRenderElement,
|
||||
}
|
||||
|
||||
impl<R> AsRenderElements<R> for CosmicWindow
|
||||
where
|
||||
R: Renderer + ImportAll,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
type RenderElement = CosmicWindowRenderElement<R>;
|
||||
fn render_elements<C: From<Self::RenderElement>>(
|
||||
&self,
|
||||
location: Point<i32, Physical>,
|
||||
scale: Scale<f64>,
|
||||
) -> Vec<C> {
|
||||
AsRenderElements::<R>::render_elements::<CosmicWindowRenderElement<R>>(
|
||||
&self.window,
|
||||
location - self.window.geometry().loc.to_physical_precise_round(scale),
|
||||
scale,
|
||||
)
|
||||
.into_iter()
|
||||
.map(C::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue