update smithay, iced; reworked window for xwayland

This commit is contained in:
Victoria Brekenfeld 2023-01-16 15:12:25 +01:00
parent 47dfc85314
commit 7992ad67f6
27 changed files with 2285 additions and 1106 deletions

View file

@ -10,28 +10,24 @@ use smithay::{
renderer::{
element::{AsRenderElements, Element, RenderElement, UnderlyingStorage},
glow::GlowRenderer,
ImportAll, Renderer,
ImportAll, ImportMem, Renderer,
},
},
desktop::{space::SpaceElement, PopupManager, Window, WindowSurfaceType},
desktop::{space::SpaceElement, PopupManager, 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},
},
reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface},
space_elements,
utils::{
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
},
wayland::{
compositor::{with_states, with_surface_tree_downward, TraversalAction},
compositor::{with_surface_tree_downward, TraversalAction},
seat::WaylandFocus,
shell::xdg::XdgToplevelSurfaceRoleAttributes,
},
};
use std::{
@ -41,6 +37,8 @@ use std::{
sync::{Arc, Mutex},
};
pub mod surface;
pub use self::surface::CosmicSurface;
pub mod stack;
pub use self::stack::CosmicStack;
pub mod window;
@ -110,68 +108,48 @@ impl Hash for CosmicMapped {
}
impl CosmicMapped {
pub fn windows(&self) -> impl Iterator<Item = (Window, Point<i32, Logical>)> + '_ {
pub fn windows(&self) -> impl Iterator<Item = (CosmicSurface, 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))),
))),
CosmicMappedInternal::Stack(stack) => {
Box::new(stack.surfaces().map(|w| (w, stack.offset())))
as Box<dyn Iterator<Item = (CosmicSurface, Point<i32, Logical>)>>
}
CosmicMappedInternal::Window(window) => {
Box::new(std::iter::once((window.surface(), window.offset())))
}
_ => Box::new(std::iter::empty()),
}
}
pub fn active_window(&self) -> Window {
pub fn active_window(&self) -> CosmicSurface {
match &self.element {
CosmicMappedInternal::Stack(stack) => stack.active(),
CosmicMappedInternal::Window(win) => win.window.clone(),
CosmicMappedInternal::Window(win) => win.surface(),
_ => unreachable!(),
}
}
pub fn active_window_offset(&self) -> Rectangle<i32, Logical> {
pub fn active_window_offset(&self) -> Point<i32, Logical> {
match &self.element {
CosmicMappedInternal::Stack(stack) => stack.offset(),
CosmicMappedInternal::Window(window) => window.offset(),
_ => unreachable!(),
}
}
pub fn active_window_geometry(&self) -> Rectangle<i32, Logical> {
match &self.element {
CosmicMappedInternal::Stack(stack) => {
let location = (
0,
stack
.header
.lock()
.unwrap()
.as_ref()
.map_or(0, |header| header.height()),
);
let size = stack.active().geometry().size;
let win = stack.active();
let location = stack.offset();
let mut size = win.geometry().size;
size -= location.to_size();
Rectangle::from_loc_and_size(location, size)
}
CosmicMappedInternal::Window(win) => {
let location = (
0,
win.header
.lock()
.unwrap()
.as_ref()
.map_or(0, |header| header.height()),
);
let size = win.window.geometry().size;
let location = win.offset();
let mut size = win.geometry().size;
size -= location.to_size();
Rectangle::from_loc_and_size(location, size)
}
_ => unreachable!(),
@ -186,13 +164,13 @@ impl CosmicMapped {
.cloned()
}
pub fn set_active(&self, window: &Window) {
pub fn set_active(&self, window: &CosmicSurface) {
if let CosmicMappedInternal::Stack(stack) = &self.element {
stack.set_active(window);
}
}
pub fn focus_window(&self, window: &Window) {
pub fn focus_window(&self, window: &CosmicSurface) {
match &self.element {
CosmicMappedInternal::Stack(stack) => stack.set_active(window),
_ => {}
@ -201,10 +179,10 @@ impl CosmicMapped {
pub fn has_surface(&self, surface: &WlSurface, surface_type: WindowSurfaceType) -> bool {
self.windows().any(|(w, _)| {
let toplevel = w.toplevel().wl_surface();
let Some(toplevel ) = w.wl_surface() else { return false };
if surface_type.contains(WindowSurfaceType::TOPLEVEL) {
if toplevel == surface {
if toplevel == *surface {
return true;
}
}
@ -214,7 +192,7 @@ impl CosmicMapped {
let found = AtomicBool::new(false);
with_surface_tree_downward(
toplevel,
&toplevel,
surface,
|_, _, search| TraversalAction::DoChildren(search),
|s, _, search| {
@ -228,7 +206,7 @@ impl CosmicMapped {
}
if surface_type.contains(WindowSurfaceType::POPUP) {
PopupManager::popups_for_surface(toplevel).any(|(p, _)| p.wl_surface() == surface)
PopupManager::popups_for_surface(&toplevel).any(|(p, _)| p.wl_surface() == surface)
} else {
false
}
@ -247,158 +225,110 @@ impl CosmicMapped {
pub fn set_resizing(&self, resizing: bool) {
for window in match &self.element {
CosmicMappedInternal::Stack(s) => {
Box::new(s.windows()) as Box<dyn Iterator<Item = Window>>
Box::new(s.surfaces()) as Box<dyn Iterator<Item = CosmicSurface>>
}
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())),
_ => unreachable!(),
} {
window.toplevel().with_pending_state(|state| {
if resizing {
state.states.set(XdgState::Resizing);
} else {
state.states.unset(XdgState::Resizing);
}
});
window.set_resizing(resizing);
}
}
pub fn is_resizing(&self) -> bool {
pub fn is_resizing(&self) -> Option<bool> {
let window = match &self.element {
CosmicMappedInternal::Stack(s) => s.active(),
CosmicMappedInternal::Window(w) => w.window.clone(),
CosmicMappedInternal::Window(w) => w.surface(),
_ => unreachable!(),
};
let xdg = window.toplevel();
xdg.current_state().states.contains(XdgState::Resizing)
|| xdg.with_pending_state(|states| states.states.contains(XdgState::Resizing))
window.is_resizing()
}
pub fn set_tiled(&self, tiled: bool) {
for xdg in match &self.element {
for window in match &self.element {
// we use the tiled state of stack windows anyway to get rid of decorations
CosmicMappedInternal::Stack(_) => None,
CosmicMappedInternal::Window(w) => Some(w.window.toplevel()),
CosmicMappedInternal::Window(w) => Some(w.surface()),
_ => unreachable!(),
} {
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);
}
});
window.set_tiled(tiled)
}
}
pub fn is_tiled(&self) -> bool {
pub fn is_tiled(&self) -> Option<bool> {
let window = match &self.element {
CosmicMappedInternal::Stack(s) => s.active(),
CosmicMappedInternal::Window(w) => w.window.clone(),
CosmicMappedInternal::Window(w) => w.surface(),
_ => unreachable!(),
};
window
.toplevel()
.current_state()
.states
.contains(XdgState::TiledLeft)
window.is_tiled()
}
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>>
Box::new(s.surfaces()) as Box<dyn Iterator<Item = CosmicSurface>>
}
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())),
_ => unreachable!(),
} {
window.toplevel().with_pending_state(|state| {
if fullscreen {
state.states.set(XdgState::Fullscreen);
} else {
state.states.unset(XdgState::Fullscreen);
}
});
window.set_fullscreen(fullscreen);
}
}
pub fn is_fullscreen(&self) -> bool {
let window = match &self.element {
CosmicMappedInternal::Stack(s) => s.active(),
CosmicMappedInternal::Window(w) => w.window.clone(),
CosmicMappedInternal::Window(w) => w.surface(),
_ => unreachable!(),
};
let xdg = window.toplevel();
xdg.current_state().states.contains(XdgState::Fullscreen)
|| xdg.with_pending_state(|states| states.states.contains(XdgState::Fullscreen))
window.is_fullscreen()
}
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>>
Box::new(s.surfaces()) as Box<dyn Iterator<Item = CosmicSurface>>
}
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())),
_ => unreachable!(),
} {
window.toplevel().with_pending_state(|state| {
if maximized {
state.states.set(XdgState::Maximized);
} else {
state.states.unset(XdgState::Maximized);
}
});
window.set_maximized(maximized)
}
}
pub fn is_maximized(&self) -> bool {
let window = match &self.element {
CosmicMappedInternal::Stack(s) => s.active(),
CosmicMappedInternal::Window(w) => w.window.clone(),
CosmicMappedInternal::Window(w) => w.surface(),
_ => unreachable!(),
};
let xdg = window.toplevel();
xdg.current_state().states.contains(XdgState::Maximized)
|| xdg.with_pending_state(|states| states.states.contains(XdgState::Maximized))
window.is_maximized()
}
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>>
Box::new(s.surfaces()) as Box<dyn Iterator<Item = CosmicSurface>>
}
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())),
_ => unreachable!(),
} {
window.toplevel().with_pending_state(|state| {
if activated {
state.states.set(XdgState::Activated);
} else {
state.states.unset(XdgState::Activated);
}
});
window.set_activated(activated)
}
}
pub fn is_activated(&self) -> bool {
let window = match &self.element {
CosmicMappedInternal::Stack(s) => s.active(),
CosmicMappedInternal::Window(w) => w.window.clone(),
CosmicMappedInternal::Window(w) => w.surface(),
_ => unreachable!(),
};
let xdg = window.toplevel();
xdg.current_state().states.contains(XdgState::Activated)
|| xdg.with_pending_state(|states| states.states.contains(XdgState::Activated))
window.is_activated()
}
pub fn set_size(&self, size: Size<i32, Logical>) {
@ -409,57 +339,34 @@ impl CosmicMapped {
}
}
pub fn min_size(&self) -> Size<i32, Logical> {
pub fn min_size(&self) -> Option<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
});
CosmicMappedInternal::Stack(stack) => {
stack.surfaces().fold(None, |min_size, window| {
let win_min_size = window.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()),
(None, None) => None,
(None, x) | (x, None) => x,
(Some(min1), Some(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
})
}
CosmicMappedInternal::Window(window) => window.surface().min_size(),
_ => unreachable!(),
}
}
pub fn max_size(&self) -> Size<i32, Logical> {
pub fn max_size(&self) -> Option<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
});
let theoretical_max = stack.surfaces().fold(None, |max_size, window| {
let win_max_size = window.max_size();
match (max_size, win_max_size) {
(None, x) => Some(x),
(Some(max1), max2) => Some(
(None, None) => None,
(None, x) | (x, None) => x,
(Some(max1), Some(max2)) => Some(
(
if max1.w == 0 {
max2.w
@ -483,21 +390,12 @@ impl CosmicMapped {
// 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(),
(None, _) => None,
(Some(max), None) => Some(max),
(Some(max), Some(min)) => Some((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
})
}
CosmicMappedInternal::Window(window) => window.surface().max_size(),
_ => unreachable!(),
}
}
@ -505,23 +403,23 @@ impl CosmicMapped {
pub fn configure(&self) {
for window in match &self.element {
CosmicMappedInternal::Stack(s) => {
Box::new(s.windows()) as Box<dyn Iterator<Item = Window>>
Box::new(s.surfaces()) as Box<dyn Iterator<Item = CosmicSurface>>
}
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())),
CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())),
_ => unreachable!(),
} {
window.toplevel().send_configure();
window.send_configure();
}
}
pub fn send_close(&self) {
let window = match &self.element {
CosmicMappedInternal::Stack(s) => s.active(),
CosmicMappedInternal::Window(w) => w.window.clone(),
CosmicMappedInternal::Window(w) => w.surface(),
_ => unreachable!(),
};
window.toplevel().send_close();
window.close();
}
#[cfg(feature = "debug")]
@ -680,7 +578,7 @@ impl PointerTarget<State> for CosmicMapped {
impl WaylandFocus for CosmicMapped {
fn wl_surface(&self) -> Option<WlSurface> {
match &self.element {
CosmicMappedInternal::Window(w) => w.window.wl_surface().clone(),
CosmicMappedInternal::Window(w) => w.surface().wl_surface().clone(),
CosmicMappedInternal::Stack(s) => s.active().wl_surface().clone(),
_ => None,
}
@ -688,8 +586,8 @@ impl WaylandFocus for CosmicMapped {
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)),
CosmicMappedInternal::Window(w) => w.surface().same_client_as(object_id),
CosmicMappedInternal::Stack(s) => s.surfaces().any(|w| w.same_client_as(object_id)),
_ => false,
}
}
@ -725,7 +623,7 @@ impl From<CosmicStack> for CosmicMapped {
pub enum CosmicMappedRenderElement<R>
where
R: AsGlowRenderer + Renderer + ImportAll,
R: AsGlowRenderer + Renderer + ImportAll + ImportMem,
<R as Renderer>::TextureId: 'static,
{
Stack(self::stack::CosmicStackRenderElement<R>),
@ -736,7 +634,7 @@ where
impl<R> Element for CosmicMappedRenderElement<R>
where
R: AsGlowRenderer + Renderer + ImportAll,
R: AsGlowRenderer + Renderer + ImportAll + ImportMem,
<R as Renderer>::TextureId: 'static,
{
fn id(&self) -> &smithay::backend::renderer::element::Id {
@ -892,7 +790,7 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicMappedRenderElement<GlMult
impl<R> From<stack::CosmicStackRenderElement<R>> for CosmicMappedRenderElement<R>
where
R: Renderer + ImportAll + AsGlowRenderer,
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
@ -902,7 +800,7 @@ where
}
impl<R> From<window::CosmicWindowRenderElement<R>> for CosmicMappedRenderElement<R>
where
R: Renderer + ImportAll + AsGlowRenderer,
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
@ -924,7 +822,7 @@ where
impl<R> AsRenderElements<R> for CosmicMapped
where
R: Renderer + ImportAll + AsGlowRenderer,
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{

View file

@ -1,16 +1,24 @@
use crate::{
state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions,
state::State,
utils::iced::{IcedElement, Program},
utils::prelude::SeatExt,
wayland::handlers::screencopy::ScreencopySessions,
};
use calloop::LoopHandle;
use cosmic::Element;
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
use smithay::{
backend::{
input::KeyState,
renderer::{
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
ImportAll, Renderer,
element::{
memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement,
AsRenderElements,
},
ImportAll, ImportMem, Renderer,
},
},
desktop::{space::SpaceElement, Window},
desktop::space::SpaceElement,
input::{
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget},
@ -21,84 +29,136 @@ use smithay::{
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
};
use std::{
fmt,
hash::Hash,
sync::{
atomic::{AtomicUsize, Ordering},
atomic::{AtomicU8, AtomicUsize, Ordering},
Arc, Mutex,
},
};
use super::CosmicSurface;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct CosmicStack(IcedElement<CosmicStackInternal>);
impl fmt::Debug for CosmicStack {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.with_program(|stack| {
f.debug_struct("CosmicStack")
.field("internal", stack)
.finish_non_exhaustive()
})
}
}
#[derive(Debug, Clone)]
pub struct CosmicStack {
windows: Arc<Mutex<Vec<Window>>>,
pub struct CosmicStackInternal {
windows: Arc<Mutex<Vec<CosmicSurface>>>,
active: Arc<AtomicUsize>,
last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>,
previous_keyboard: Arc<AtomicUsize>,
pointer_entered: Option<Arc<AtomicU8>>,
previous_pointer: Arc<AtomicUsize>,
pub(super) header: Arc<Mutex<Option<HeaderBar>>>,
last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>,
}
impl PartialEq for CosmicStack {
fn eq(&self, other: &Self) -> bool {
*self.windows.lock().unwrap() == *other.windows.lock().unwrap()
impl CosmicStackInternal {
pub fn swap_focus(&self, focus: Focus) -> Focus {
if let Some(pointer_entered) = self.pointer_entered.as_ref() {
unsafe {
std::mem::transmute::<u8, Focus>(
pointer_entered.swap(focus as u8, Ordering::SeqCst),
)
}
} else {
Focus::Window
}
}
pub fn current_focus(&self) -> Focus {
if let Some(pointer_entered) = self.pointer_entered.as_ref() {
unsafe { std::mem::transmute::<u8, Focus>(pointer_entered.load(Ordering::SeqCst)) }
} else {
Focus::Window
}
}
}
impl Eq for CosmicStack {}
const TAB_HEIGHT: i32 = 24;
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
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Focus {
None,
Header,
Window,
}
impl CosmicStack {
pub fn active(&self) -> Window {
self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)].clone()
pub fn new(
window: impl Into<CosmicSurface>,
handle: LoopHandle<'static, crate::state::Data>,
) -> CosmicStack {
let window = window.into();
let width = window.geometry().size.w;
CosmicStack(IcedElement::new(
CosmicStackInternal {
windows: Arc::new(Mutex::new(vec![window])),
active: Arc::new(AtomicUsize::new(0)),
previous_keyboard: Arc::new(AtomicUsize::new(0)),
pointer_entered: None,
previous_pointer: Arc::new(AtomicUsize::new(0)),
last_location: Arc::new(Mutex::new(None)),
},
(width, TAB_HEIGHT),
handle,
))
}
pub fn set_active(&self, window: &Window) {
if let Some(val) = self
.windows
.lock()
.unwrap()
.iter()
.position(|w| w == window)
{
let old = self.active.swap(val, Ordering::SeqCst);
self.previous_keyboard.store(old, Ordering::SeqCst);
self.previous_pointer.store(old, Ordering::SeqCst);
}
//pub fn add_window()
//pub fn remove_window()
//pub fn len
pub fn active(&self) -> CosmicSurface {
self.0
.with_program(|p| p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].clone())
}
pub fn set_active(&self, window: &CosmicSurface) {
self.0.with_program(|p| {
if let Some(val) = p.windows.lock().unwrap().iter().position(|w| w == window) {
let old = p.active.swap(val, Ordering::SeqCst);
p.previous_keyboard.store(old, Ordering::SeqCst);
p.previous_pointer.store(old, Ordering::SeqCst);
}
})
}
pub fn surfaces(&self) -> impl Iterator<Item = CosmicSurface> {
self.0.with_program(|p| {
p.windows
.lock()
.unwrap()
.iter()
.cloned()
.collect::<Vec<_>>()
.into_iter()
})
}
pub fn offset(&self) -> Point<i32, Logical> {
Point::from((0, TAB_HEIGHT))
}
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();
self.0.with_program(|p| {
let surface_size = (size.w, size.h - TAB_HEIGHT).into();
for window in self.windows.lock().unwrap().iter() {
window
.toplevel()
.with_pending_state(|state| state.size = Some(surface_size));
}
for window in p.windows.lock().unwrap().iter() {
window.set_size(surface_size);
}
});
self.0.resize(Size::from((size.w, TAB_HEIGHT)));
}
fn keyboard_leave_if_previous(
@ -107,13 +167,21 @@ impl CosmicStack {
data: &mut State,
serial: Serial,
) -> usize {
let active = self.active.load(Ordering::SeqCst);
let previous = self.previous_keyboard.swap(active, Ordering::SeqCst);
if previous != active {
KeyboardTarget::leave(&self.windows.lock().unwrap()[previous], seat, data, serial);
// TODO: KeyboardTarget::enter(&self.windows.lock().unwrap()[active], seat, data, serial, seat.keys())
}
active
self.0.with_program(|p| {
let active = p.active.load(Ordering::SeqCst);
let previous = p.previous_keyboard.swap(active, Ordering::SeqCst);
if previous != active {
KeyboardTarget::leave(&p.windows.lock().unwrap()[previous], seat, data, serial);
KeyboardTarget::enter(
&p.windows.lock().unwrap()[active],
seat,
data,
Vec::new(), //seat.keys(),
serial,
)
}
active
})
}
fn pointer_leave_if_previous(
@ -124,100 +192,141 @@ impl CosmicStack {
time: u32,
location: Point<f64, Logical>,
) -> usize {
let active = self.active.load(Ordering::SeqCst);
let previous = self.previous_pointer.swap(active, Ordering::SeqCst);
if previous != active {
if let Some(sessions) = self.windows.lock().unwrap()[previous]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
session.cursor_leave(seat, InputType::Pointer)
self.0.with_program(|p| {
let active = p.active.load(Ordering::SeqCst);
let previous = p.previous_pointer.swap(active, Ordering::SeqCst);
if previous != active {
if let Some(sessions) = p.windows.lock().unwrap()[previous]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
session.cursor_leave(seat, InputType::Pointer)
}
}
}
PointerTarget::leave(
&self.windows.lock().unwrap()[previous],
seat,
data,
serial,
time,
);
if let Some(sessions) = self.windows.lock().unwrap()[active]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer)
}
}
PointerTarget::enter(
&self.windows.lock().unwrap()[active],
seat,
data,
&MotionEvent {
location,
PointerTarget::leave(
&p.windows.lock().unwrap()[previous],
seat,
data,
serial,
time,
},
);
}
active
);
if let Some(sessions) = p.windows.lock().unwrap()[active]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer)
}
}
PointerTarget::enter(
&p.windows.lock().unwrap()[active],
seat,
data,
&MotionEvent {
location,
serial,
time,
},
);
}
active
})
}
}
impl Program for CosmicStackInternal {
type Message = ();
fn view(&self) -> Element<'_, Self::Message> {
cosmic::iced::widget::text("TODO").into()
}
}
impl IsAlive for CosmicStack {
fn alive(&self) -> bool {
self.windows.lock().unwrap().iter().any(IsAlive::alive)
self.0
.with_program(|p| p.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)])
self.0.with_program(|p| {
let mut bbox =
SpaceElement::bbox(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]);
bbox.size.h += TAB_HEIGHT;
bbox
})
}
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,
)
let mut point = *point;
if point.y < TAB_HEIGHT as f64 {
return true;
}
point.y -= TAB_HEIGHT as f64;
self.0.with_program(|p| {
SpaceElement::is_in_input_region(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
&point,
)
})
}
fn set_activate(&self, activated: bool) {
self.windows
.lock()
.unwrap()
.iter()
.for_each(|w| SpaceElement::set_activate(w, activated))
SpaceElement::set_activate(&self.0, activated);
self.0.with_program(|p| {
p.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))
SpaceElement::output_enter(&self.0, output, overlap);
self.0.with_program(|p| {
p.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))
SpaceElement::output_leave(&self.0, output);
self.0.with_program(|p| {
p.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)])
self.0.with_program(|p| {
let mut geo =
SpaceElement::geometry(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]);
geo.size.h += TAB_HEIGHT;
geo
})
}
fn z_index(&self) -> u8 {
SpaceElement::z_index(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)])
self.0.with_program(|p| {
SpaceElement::z_index(&p.windows.lock().unwrap()[p.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))
self.0.with_program(|p| {
let mut windows = p.windows.lock().unwrap();
windows.retain(IsAlive::alive); // TODO: We don't handle empty stacks properly
let len = windows.len();
let _ = p
.active
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| {
(active > len).then_some(len - 1)
});
windows.iter().for_each(|w| SpaceElement::refresh(w))
})
}
}
@ -229,19 +338,23 @@ impl KeyboardTarget<State> for CosmicStack {
keys: Vec<KeysymHandle<'_>>,
serial: Serial,
) {
let active = self.active.load(Ordering::SeqCst);
self.previous_keyboard.store(active, Ordering::SeqCst);
KeyboardTarget::enter(
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
seat,
data,
keys,
serial,
)
self.0.with_program(|p| {
let active = p.active.load(Ordering::SeqCst);
p.previous_keyboard.store(active, Ordering::SeqCst);
KeyboardTarget::enter(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
seat,
data,
keys,
serial,
)
})
}
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
let active = self.keyboard_leave_if_previous(seat, data, serial);
KeyboardTarget::leave(&self.windows.lock().unwrap()[active], seat, data, serial)
self.0.with_program(|p| {
KeyboardTarget::leave(&p.windows.lock().unwrap()[active], seat, data, serial)
})
}
fn key(
&self,
@ -253,15 +366,17 @@ impl KeyboardTarget<State> for CosmicStack {
time: u32,
) {
let active = self.keyboard_leave_if_previous(seat, data, serial);
KeyboardTarget::key(
&self.windows.lock().unwrap()[active],
seat,
data,
key,
state,
serial,
time,
)
self.0.with_program(|p| {
KeyboardTarget::key(
&p.windows.lock().unwrap()[active],
seat,
data,
key,
state,
serial,
time,
)
})
}
fn modifiers(
&self,
@ -271,132 +386,225 @@ impl KeyboardTarget<State> for CosmicStack {
serial: Serial,
) {
let active = self.keyboard_leave_if_previous(seat, data, serial);
KeyboardTarget::modifiers(
&self.windows.lock().unwrap()[active],
seat,
data,
modifiers,
serial,
)
self.0.with_program(|p| {
KeyboardTarget::modifiers(
&p.windows.lock().unwrap()[active],
seat,
data,
modifiers,
serial,
)
})
}
}
impl PointerTarget<State> for CosmicStack {
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
if let Some(sessions) = self.active().user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer)
if self.0.with_program(|p| {
if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer)
}
}
if event.location.y < TAB_HEIGHT as f64 {
let focus = p.swap_focus(Focus::Header);
assert_eq!(focus, Focus::None);
true
} else {
let focus = p.swap_focus(Focus::Window);
assert_eq!(focus, Focus::None);
*p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
let active = p.active.load(Ordering::SeqCst);
p.previous_pointer.store(active, Ordering::SeqCst);
PointerTarget::enter(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
seat,
data,
event,
);
false
}
}) {
PointerTarget::enter(&self.0, seat, data, event)
}
*self.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
let active = self.active.load(Ordering::SeqCst);
self.previous_pointer.store(active, Ordering::SeqCst);
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) {
let active =
self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location);
if let Some(sessions) = self.active().user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
if let Some((geo, hotspot)) =
seat.cursor_geometry(buffer_loc, data.common.clock.now())
{
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
if let Some((previous, next)) = self.0.with_program(|p| {
if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
if let Some((geo, hotspot)) =
seat.cursor_geometry(buffer_loc, data.common.clock.now())
{
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
}
}
}
}
PointerTarget::motion(&self.windows.lock().unwrap()[active], seat, data, event)
if event.location.y < TAB_HEIGHT as f64 {
let previous = p.swap_focus(Focus::Header);
if previous == Focus::Window {
PointerTarget::leave(
&p.windows.lock().unwrap()[active],
seat,
data,
event.serial,
event.time,
);
}
Some((previous, Focus::Header))
} else {
let mut event = event.clone();
event.location.y -= TAB_HEIGHT as f64;
let previous = p.swap_focus(Focus::Window);
if previous != Focus::Window {
PointerTarget::enter(&p.windows.lock().unwrap()[active], seat, data, &event);
} else {
PointerTarget::motion(&p.windows.lock().unwrap()[active], seat, data, &event);
}
Some((previous, Focus::Window))
}
}) {
match (previous, next) {
(Focus::Header, Focus::Header) => PointerTarget::motion(&self.0, seat, data, event),
(_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, event),
(Focus::Header, _) => {
PointerTarget::leave(&self.0, seat, data, event.serial, event.time)
}
_ => {}
}
}
}
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
if let Some((location, _serial, _time)) = self.last_location.lock().unwrap().clone() {
if let Some((location, _serial, _time)) = self
.0
.with_program(|p| p.last_location.lock().unwrap().clone())
{
self.pointer_leave_if_previous(seat, data, event.serial, event.time, location);
}
PointerTarget::button(
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
seat,
data,
event,
)
match self.0.with_program(|p| p.current_focus()) {
Focus::Header => PointerTarget::button(&self.0, seat, data, event),
Focus::Window => self.0.with_program(|p| {
PointerTarget::button(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
seat,
data,
event,
)
}),
_ => {}
}
}
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
if let Some((location, serial, time)) = self.last_location.lock().unwrap().clone() {
if let Some((location, serial, time)) = self
.0
.with_program(|p| p.last_location.lock().unwrap().clone())
{
self.pointer_leave_if_previous(seat, data, serial, time, location);
}
PointerTarget::axis(
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
seat,
data,
frame,
)
match self.0.with_program(|p| p.current_focus()) {
Focus::Header => PointerTarget::axis(&self.0, seat, data, frame),
Focus::Window => self.0.with_program(|p| {
PointerTarget::axis(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
seat,
data,
frame,
)
}),
_ => {}
}
}
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
if let Some((location, serial, time)) = self.last_location.lock().unwrap().clone() {
if let Some((location, serial, time)) = self
.0
.with_program(|p| p.last_location.lock().unwrap().clone())
{
self.pointer_leave_if_previous(seat, data, serial, time, location);
}
if let Some(sessions) = self.active().user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
session.cursor_leave(seat, InputType::Pointer)
}
}
PointerTarget::leave(
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
seat,
data,
serial,
time,
)
let previous = self.0.with_program(|p| {
if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]
.user_data()
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() {
session.cursor_leave(seat, InputType::Pointer)
}
}
p.swap_focus(Focus::None)
});
assert!(previous != Focus::None);
match previous {
Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time),
Focus::Window => self.0.with_program(|p| {
PointerTarget::leave(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
seat,
data,
serial,
time,
)
}),
_ => {}
}
}
}
render_elements! {
pub CosmicStackRenderElement<R> where R: ImportAll;
pub CosmicStackRenderElement<R> where R: ImportAll + ImportMem;
Header=MemoryRenderBufferRenderElement<R>,
Window=WaylandSurfaceRenderElement<R>,
}
impl<R> AsRenderElements<R> for CosmicStack
where
R: Renderer + ImportAll,
R: Renderer + ImportAll + ImportMem,
<R as Renderer>::TextureId: 'static,
{
type RenderElement = CosmicStackRenderElement<R>;
fn render_elements<C: From<Self::RenderElement>>(
&self,
renderer: &mut R,
location: Point<i32, Physical>,
mut location: Point<i32, Physical>,
scale: Scale<f64>,
) -> Vec<C> {
AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
renderer,
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()
let mut elements = AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
&self.0, renderer, location, scale,
);
location.y += TAB_HEIGHT;
elements.extend(self.0.with_program(|p| {
let elements = AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
renderer,
location,
scale,
);
elements
}));
elements.into_iter().map(C::from).collect()
}
}

View file

@ -0,0 +1,579 @@
use std::time::Duration;
use smithay::{
backend::renderer::{
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
ImportAll, Renderer,
},
desktop::{
utils::{
send_frames_surface_tree, take_presentation_feedback_surface_tree,
with_surfaces_surface_tree, OutputPresentationFeedback,
},
Window,
},
input::{keyboard::KeyboardTarget, pointer::PointerTarget},
output::Output,
reexports::{
wayland_protocols::{
wp::presentation_time::server::wp_presentation_feedback::Kind,
xdg::{
decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode,
shell::server::xdg_toplevel::State as ToplevelState,
},
},
wayland_server::protocol::wl_surface::WlSurface,
},
space_elements,
utils::{user_data::UserDataMap, Logical, Rectangle, Size},
wayland::{
compositor::{with_states, SurfaceData},
seat::WaylandFocus,
shell::xdg::XdgToplevelSurfaceData,
},
xwayland::X11Surface,
};
space_elements! {
#[derive(Debug, Clone, PartialEq)]
pub CosmicSurface;
Wayland=Window,
X11=X11Surface,
}
pub const SSD_HEIGHT: i32 = 48;
impl CosmicSurface {
pub fn title(&self) -> String {
match self {
CosmicSurface::Wayland(window) => {
with_states(window.toplevel().wl_surface(), |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap()
.title
.clone()
.unwrap_or_default()
})
}
CosmicSurface::X11(surface) => surface.title(),
_ => unreachable!(),
}
}
pub fn app_id(&self) -> String {
match self {
CosmicSurface::Wayland(window) => {
with_states(window.toplevel().wl_surface(), |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap()
.app_id
.clone()
.unwrap_or_default()
})
}
CosmicSurface::X11(surface) => surface.class(),
_ => unreachable!(),
}
}
pub fn set_size(&self, size: Size<i32, Logical>) {
match self {
CosmicSurface::Wayland(window) => window
.toplevel()
.with_pending_state(|state| state.size = Some(size)),
CosmicSurface::X11(surface) => {
let rect = Rectangle::from_loc_and_size(surface.geometry().loc, size);
let _ = surface.configure(rect);
}
_ => {}
}
}
pub fn is_activated(&self) -> bool {
match self {
CosmicSurface::Wayland(window) => window
.toplevel()
.current_state()
.states
.contains(ToplevelState::Activated),
CosmicSurface::X11(surface) => surface.is_activated(),
_ => unreachable!(),
}
}
pub fn set_activated(&self, activated: bool) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| {
if activated {
state.states.set(ToplevelState::Activated);
} else {
state.states.unset(ToplevelState::Activated);
}
}),
CosmicSurface::X11(surface) => {
let _ = surface.set_activated(activated);
}
_ => unreachable!(),
}
}
pub fn is_decorated(&self) -> bool {
match self {
CosmicSurface::Wayland(window) => window
.toplevel()
.current_state()
.decoration_mode
.map(|mode| mode == DecorationMode::ClientSide)
.unwrap_or(true),
CosmicSurface::X11(surface) => surface.is_decorated(),
_ => unreachable!(),
}
}
pub fn is_resizing(&self) -> Option<bool> {
match self {
CosmicSurface::Wayland(window) => {
let xdg = window.toplevel();
Some(
xdg.current_state().states.contains(ToplevelState::Resizing)
|| xdg.with_pending_state(|states| {
states.states.contains(ToplevelState::Resizing)
}),
)
}
_ => None,
}
}
pub fn set_resizing(&self, resizing: bool) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| {
if resizing {
state.states.set(ToplevelState::Resizing);
} else {
state.states.unset(ToplevelState::Resizing);
}
}),
_ => {}
}
}
pub fn is_tiled(&self) -> Option<bool> {
match self {
CosmicSurface::Wayland(window) => Some(
window
.toplevel()
.current_state()
.states
.contains(ToplevelState::TiledLeft),
),
_ => None,
}
}
pub fn set_tiled(&self, tiled: bool) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| {
if tiled {
state.states.set(ToplevelState::TiledLeft);
state.states.set(ToplevelState::TiledRight);
state.states.set(ToplevelState::TiledTop);
state.states.set(ToplevelState::TiledBottom);
} else {
state.states.unset(ToplevelState::TiledLeft);
state.states.unset(ToplevelState::TiledRight);
state.states.unset(ToplevelState::TiledTop);
state.states.unset(ToplevelState::TiledBottom);
}
}),
_ => {}
}
}
pub fn is_fullscreen(&self) -> bool {
match self {
CosmicSurface::Wayland(window) => {
let xdg = window.toplevel();
xdg.current_state()
.states
.contains(ToplevelState::Fullscreen)
|| xdg.with_pending_state(|state| {
state.states.contains(ToplevelState::Fullscreen)
})
}
CosmicSurface::X11(surface) => surface.is_fullscreen(),
_ => unreachable!(),
}
}
pub fn set_fullscreen(&self, fullscreen: bool) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| {
if fullscreen {
state.states.set(ToplevelState::Fullscreen);
} else {
state.states.unset(ToplevelState::Fullscreen);
}
}),
CosmicSurface::X11(surface) => {
let _ = surface.set_fullscreen(fullscreen);
}
_ => unreachable!(),
}
}
pub fn is_maximized(&self) -> bool {
match self {
CosmicSurface::Wayland(window) => {
let xdg = window.toplevel();
xdg.current_state()
.states
.contains(ToplevelState::Maximized)
|| xdg
.with_pending_state(|state| state.states.contains(ToplevelState::Maximized))
}
CosmicSurface::X11(surface) => surface.is_maximized(),
_ => unreachable!(),
}
}
pub fn set_maximized(&self, maximized: bool) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| {
if maximized {
state.states.set(ToplevelState::Maximized);
} else {
state.states.unset(ToplevelState::Maximized);
}
}),
CosmicSurface::X11(surface) => {
let _ = surface.set_maximized(maximized);
}
_ => unreachable!(),
}
}
pub fn min_size(&self) -> Option<Size<i32, Logical>> {
match self {
CosmicSurface::Wayland(window) => {
Some(with_states(window.toplevel().wl_surface(), |states| {
let attrs = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
attrs.min_size
}))
.filter(|size| !(size.w == 0 && size.h == 0))
}
CosmicSurface::X11(surface) => surface.min_size(),
_ => unreachable!(),
}
}
pub fn max_size(&self) -> Option<Size<i32, Logical>> {
match self {
CosmicSurface::Wayland(window) => {
Some(with_states(window.toplevel().wl_surface(), |states| {
let attrs = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
attrs.max_size
}))
.filter(|size| !(size.w == 0 && size.h == 0))
}
CosmicSurface::X11(surface) => surface.max_size(),
_ => unreachable!(),
}
}
pub fn send_configure(&self) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().send_configure(),
CosmicSurface::X11(surface) => {
let _ = surface.configure(None);
}
_ => unreachable!(),
}
}
pub fn close(&self) {
match self {
CosmicSurface::Wayland(window) => window.toplevel().send_close(),
CosmicSurface::X11(surface) => {
let _ = surface.close();
}
_ => unreachable!(),
}
}
pub fn on_commit(&self) {
match self {
CosmicSurface::Wayland(window) => window.on_commit(),
_ => {}
}
}
pub fn send_frame<T, F>(
&self,
output: &Output,
time: T,
throttle: Option<Duration>,
primary_scan_out_output: F,
) where
T: Into<Duration>,
F: FnMut(&WlSurface, &SurfaceData) -> Option<Output> + Copy,
{
match self {
CosmicSurface::Wayland(window) => {
window.send_frame(output, time, throttle, primary_scan_out_output)
}
CosmicSurface::X11(surface) => {
if let Some(wl_surface) = surface.wl_surface() {
send_frames_surface_tree(
&wl_surface,
output,
time,
throttle,
primary_scan_out_output,
)
}
}
_ => unreachable!(),
}
}
pub fn take_presentation_feedback<F1, F2>(
&self,
output_feedback: &mut OutputPresentationFeedback,
primary_scan_out_output: F1,
presentation_feedback_flags: F2,
) where
F1: FnMut(&WlSurface, &SurfaceData) -> Option<Output> + Copy,
F2: FnMut(&WlSurface, &SurfaceData) -> Kind + Copy,
{
match self {
CosmicSurface::Wayland(window) => window.take_presentation_feedback(
output_feedback,
primary_scan_out_output,
presentation_feedback_flags,
),
CosmicSurface::X11(surface) => {
if let Some(wl_surface) = surface.wl_surface() {
take_presentation_feedback_surface_tree(
&wl_surface,
output_feedback,
primary_scan_out_output,
presentation_feedback_flags,
)
}
}
_ => unreachable!(),
}
}
pub fn with_surfaces<F>(&self, processor: F)
where
F: FnMut(&WlSurface, &SurfaceData) + Copy,
{
match self {
CosmicSurface::Wayland(window) => window.with_surfaces(processor),
CosmicSurface::X11(surface) => {
if let Some(wl_surface) = surface.wl_surface() {
with_surfaces_surface_tree(&wl_surface, processor)
}
}
_ => unreachable!(),
}
}
pub fn user_data(&self) -> &UserDataMap {
match self {
CosmicSurface::Wayland(window) => window.user_data(),
CosmicSurface::X11(surface) => surface.user_data(),
_ => unreachable!(),
}
}
}
impl KeyboardTarget<crate::state::State> for CosmicSurface {
fn enter(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
keys: Vec<smithay::input::keyboard::KeysymHandle<'_>>,
serial: smithay::utils::Serial,
) {
match self {
CosmicSurface::Wayland(window) => {
KeyboardTarget::enter(window, seat, data, keys, serial)
}
CosmicSurface::X11(surface) => KeyboardTarget::enter(surface, seat, data, keys, serial),
_ => unreachable!(),
}
}
fn leave(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
serial: smithay::utils::Serial,
) {
match self {
CosmicSurface::Wayland(window) => KeyboardTarget::leave(window, seat, data, serial),
CosmicSurface::X11(surface) => KeyboardTarget::leave(surface, seat, data, serial),
_ => unreachable!(),
}
}
fn key(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
key: smithay::input::keyboard::KeysymHandle<'_>,
state: smithay::backend::input::KeyState,
serial: smithay::utils::Serial,
time: u32,
) {
match self {
CosmicSurface::Wayland(window) => {
KeyboardTarget::key(window, seat, data, key, state, serial, time)
}
CosmicSurface::X11(surface) => {
KeyboardTarget::key(surface, seat, data, key, state, serial, time)
}
_ => unreachable!(),
}
}
fn modifiers(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
modifiers: smithay::input::keyboard::ModifiersState,
serial: smithay::utils::Serial,
) {
match self {
CosmicSurface::Wayland(window) => {
KeyboardTarget::modifiers(window, seat, data, modifiers, serial)
}
CosmicSurface::X11(surface) => {
KeyboardTarget::modifiers(surface, seat, data, modifiers, serial)
}
_ => unreachable!(),
}
}
}
impl PointerTarget<crate::state::State> for CosmicSurface {
fn enter(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
event: &smithay::input::pointer::MotionEvent,
) {
match self {
CosmicSurface::Wayland(window) => PointerTarget::enter(window, seat, data, event),
CosmicSurface::X11(surface) => PointerTarget::enter(surface, seat, data, event),
_ => unreachable!(),
}
}
fn motion(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
event: &smithay::input::pointer::MotionEvent,
) {
match self {
CosmicSurface::Wayland(window) => PointerTarget::motion(window, seat, data, event),
CosmicSurface::X11(surface) => PointerTarget::motion(surface, seat, data, event),
_ => unreachable!(),
}
}
fn button(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
event: &smithay::input::pointer::ButtonEvent,
) {
match self {
CosmicSurface::Wayland(window) => PointerTarget::button(window, seat, data, event),
CosmicSurface::X11(surface) => PointerTarget::button(surface, seat, data, event),
_ => unreachable!(),
}
}
fn axis(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
frame: smithay::input::pointer::AxisFrame,
) {
match self {
CosmicSurface::Wayland(window) => PointerTarget::axis(window, seat, data, frame),
CosmicSurface::X11(surface) => PointerTarget::axis(surface, seat, data, frame),
_ => unreachable!(),
}
}
fn leave(
&self,
seat: &smithay::input::Seat<crate::state::State>,
data: &mut crate::state::State,
serial: smithay::utils::Serial,
time: u32,
) {
match self {
CosmicSurface::Wayland(window) => {
PointerTarget::leave(window, seat, data, serial, time)
}
CosmicSurface::X11(surface) => PointerTarget::leave(surface, seat, data, serial, time),
_ => unreachable!(),
}
}
}
impl WaylandFocus for CosmicSurface {
fn wl_surface(&self) -> Option<WlSurface> {
match self {
CosmicSurface::Wayland(window) => window.wl_surface(),
CosmicSurface::X11(surface) => surface.wl_surface(),
_ => unreachable!(),
}
}
}
impl<R> AsRenderElements<R> for CosmicSurface
where
R: Renderer + ImportAll,
<R as Renderer>::TextureId: 'static,
{
type RenderElement = WaylandSurfaceRenderElement<R>;
fn render_elements<C: From<Self::RenderElement>>(
&self,
renderer: &mut R,
location: smithay::utils::Point<i32, smithay::utils::Physical>,
scale: smithay::utils::Scale<f64>,
) -> Vec<C> {
match self {
CosmicSurface::Wayland(window) => window.render_elements(renderer, location, scale),
CosmicSurface::X11(surface) => surface.render_elements(renderer, location, scale),
_ => unreachable!(),
}
}
}

View file

@ -1,152 +1,280 @@
use crate::{
state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions,
state::State,
utils::{
iced::{IcedElement, Program},
prelude::SeatExt,
},
wayland::handlers::screencopy::ScreencopySessions,
};
use calloop::LoopHandle;
use cosmic::{iced_native::Command, Element};
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
use iced_softbuffer::native::raqote::{DrawOptions, DrawTarget, PathBuilder, SolidSource, Source};
use smithay::{
backend::{
input::KeyState,
renderer::{
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
ImportAll, Renderer,
element::{
memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement,
AsRenderElements,
},
ImportAll, ImportMem, Renderer,
},
},
desktop::{space::SpaceElement, Window},
desktop::space::SpaceElement,
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::{
fmt,
hash::Hash,
sync::{Arc, Mutex},
sync::{
atomic::{AtomicU8, Ordering},
Arc,
},
};
use super::{surface::SSD_HEIGHT, CosmicSurface};
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct CosmicWindow(IcedElement<CosmicWindowInternal>);
impl fmt::Debug for CosmicWindow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.with_program(|window| {
f.debug_struct("CosmicWindow")
.field("internal", window)
.finish_non_exhaustive()
})
}
}
#[derive(Debug, Clone)]
pub struct CosmicWindow {
pub(super) window: Window,
pub(super) header: Arc<Mutex<Option<HeaderBar>>>,
pub struct CosmicWindowInternal {
pub(super) window: CosmicSurface,
pointer_entered: Option<Arc<AtomicU8>>,
}
impl PartialEq<Window> for CosmicWindow {
fn eq(&self, other: &Window) -> bool {
&self.window == other
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Focus {
None,
Header,
Window,
}
impl CosmicWindowInternal {
pub fn swap_focus(&self, focus: Focus) -> Focus {
if let Some(pointer_entered) = self.pointer_entered.as_ref() {
unsafe {
std::mem::transmute::<u8, Focus>(
pointer_entered.swap(focus as u8, Ordering::SeqCst),
)
}
} else {
Focus::Window
}
}
}
impl PartialEq<CosmicWindow> for Window {
fn eq(&self, other: &CosmicWindow) -> bool {
self == &other.window
pub fn current_focus(&self) -> Focus {
if let Some(pointer_entered) = self.pointer_entered.as_ref() {
unsafe { std::mem::transmute::<u8, Focus>(pointer_entered.load(Ordering::SeqCst)) }
} else {
Focus::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)
pub fn has_ssd(&self) -> bool {
self.pointer_entered.is_some()
}
}
impl CosmicWindow {
pub fn new(
window: impl Into<CosmicSurface>,
handle: LoopHandle<'static, crate::state::Data>,
) -> CosmicWindow {
let window = window.into();
let needs_ssd = !window.is_decorated();
let width = window.geometry().size.w;
CosmicWindow(IcedElement::new(
CosmicWindowInternal {
window,
pointer_entered: if needs_ssd {
Some(Arc::new(AtomicU8::new(Focus::None as u8)))
} else {
None
},
},
(width, SSD_HEIGHT),
handle,
))
}
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();
self.window
.toplevel()
.with_pending_state(|state| state.size = Some(surface_size));
self.0.with_program(|p| {
let surface_size = (size.w, size.h - if p.has_ssd() { SSD_HEIGHT } else { 0 }).into();
p.window.set_size(surface_size)
});
self.0.resize(Size::from((size.w, SSD_HEIGHT)));
}
}
impl From<Window> for CosmicWindow {
fn from(window: Window) -> Self {
let is_ssd = matches!(
window.toplevel().current_state().decoration_mode,
Some(DecorationMode::ServerSide)
);
CosmicWindow {
window,
header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))),
pub fn surface(&self) -> CosmicSurface {
self.0.with_program(|p| p.window.clone())
}
pub fn offset(&self) -> Point<i32, Logical> {
let has_ssd = self.0.with_program(|p| p.has_ssd());
if has_ssd {
Point::from((0, SSD_HEIGHT))
} else {
Point::from((0, 0))
}
}
}
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(surf),
header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))),
#[derive(Debug, Clone, Copy)]
pub enum Message {
DragStart,
Maximize,
Close,
}
impl Program for CosmicWindowInternal {
type Message = Message;
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
/*
match message {
Message::DragStart => match &self.window {
CosmicWindowSurface::Wayland(window) => self
.with_program(|internal| internal.loop_handle())
.insert_idle(|data| {}),
CosmicWindowSurface::X11(surface) => {}
_ => unreachable!(),
},
}
*/
Command::none()
}
fn background(&self, target: &mut DrawTarget<&mut [u32]>) {
let radius = 8.;
let (w, h) = (target.width() as f32, target.height() as f32);
let mut pb = PathBuilder::new();
pb.move_to(0., h); // lower-left
// upper-left rounded corner
pb.line_to(0., radius);
pb.quad_to(0., 0., radius, 0.);
// upper-right rounded corner
pb.line_to(w - radius, 0.);
pb.quad_to(w, 0., w, radius);
pb.line_to(w, h); // lower-right
let path = pb.finish();
target.push_clip(&path);
if self.window.is_activated() {
target.clear(SolidSource::from_unpremultiplied_argb(u8::MAX, 30, 30, 30));
} else {
target.clear(SolidSource::from_unpremultiplied_argb(u8::MAX, 39, 39, 39));
}
}
}
#[derive(Debug, Default, PartialEq)]
pub(super) struct HeaderBar {
pointer_loc: Option<Point<f64, Logical>>,
close_button_hover: bool,
maximize_button_hover: bool,
}
fn view(&self) -> Element<'_, Self::Message> {
cosmic::widget::header_bar()
.title(self.window.title())
.on_drag(Message::DragStart)
.on_maximize(Message::Maximize)
.on_close(Message::Close)
.into_element()
}
impl HeaderBar {
pub fn height(&self) -> i32 {
0
fn foreground(&self, target: &mut DrawTarget<&mut [u32]>) {
if !self.window.is_activated() {
let (w, h) = (target.width() as f32, target.height() as f32);
let mut options = DrawOptions::new();
options.alpha = 0.4;
target.fill_rect(
0.,
0.,
w,
h,
&Source::Solid(SolidSource::from_unpremultiplied_argb(u8::MAX, 0, 0, 0)),
&options,
);
}
}
}
impl IsAlive for CosmicWindow {
fn alive(&self) -> bool {
self.window.alive()
self.0.with_program(|p| p.window.alive())
}
}
impl SpaceElement for CosmicWindow {
fn bbox(&self) -> Rectangle<i32, Logical> {
SpaceElement::bbox(&self.window)
self.0.with_program(|p| {
let mut bbox = SpaceElement::bbox(&p.window);
if p.has_ssd() {
bbox.size.h += SSD_HEIGHT;
}
bbox
})
}
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
SpaceElement::is_in_input_region(&self.window, point)
let mut point = *point;
self.0.with_program(|p| {
if p.has_ssd() {
if point.y < SSD_HEIGHT as f64 {
return true;
} else {
point.y -= SSD_HEIGHT as f64;
}
}
SpaceElement::is_in_input_region(&p.window, &point)
})
}
fn set_activate(&self, activated: bool) {
SpaceElement::set_activate(&self.window, activated)
SpaceElement::set_activate(&self.0, activated);
self.0
.with_program(|p| SpaceElement::set_activate(&p.window, activated));
}
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
SpaceElement::output_enter(&self.window, output, overlap)
SpaceElement::output_enter(&self.0, output, overlap);
self.0
.with_program(|p| SpaceElement::output_enter(&p.window, output, overlap));
}
fn output_leave(&self, output: &Output) {
SpaceElement::output_leave(&self.window, output)
SpaceElement::output_leave(&self.0, output);
self.0
.with_program(|p| SpaceElement::output_leave(&p.window, output));
}
fn geometry(&self) -> Rectangle<i32, Logical> {
SpaceElement::geometry(&self.window)
self.0.with_program(|p| {
let mut geo = SpaceElement::geometry(&p.window);
if p.has_ssd() {
geo.size.h += SSD_HEIGHT;
}
geo
})
}
fn z_index(&self) -> u8 {
SpaceElement::z_index(&self.window)
self.0.with_program(|p| SpaceElement::z_index(&p.window))
}
fn refresh(&self) {
SpaceElement::refresh(&self.window)
self.0.with_program(|p| SpaceElement::refresh(&p.window))
}
}
@ -158,10 +286,12 @@ impl KeyboardTarget<State> for CosmicWindow {
keys: Vec<KeysymHandle<'_>>,
serial: Serial,
) {
KeyboardTarget::enter(&self.window, seat, data, keys, serial)
self.0
.with_program(|p| KeyboardTarget::enter(&p.window, seat, data, keys, serial))
}
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
KeyboardTarget::leave(&self.window, seat, data, serial)
self.0
.with_program(|p| KeyboardTarget::leave(&p.window, seat, data, serial))
}
fn key(
&self,
@ -172,7 +302,8 @@ impl KeyboardTarget<State> for CosmicWindow {
serial: Serial,
time: u32,
) {
KeyboardTarget::key(&self.window, seat, data, key, state, serial, time)
self.0
.with_program(|p| KeyboardTarget::key(&p.window, seat, data, key, state, serial, time))
}
fn modifiers(
&self,
@ -181,82 +312,169 @@ impl KeyboardTarget<State> for CosmicWindow {
modifiers: ModifiersState,
serial: Serial,
) {
KeyboardTarget::modifiers(&self.window, seat, data, modifiers, serial)
self.0
.with_program(|p| KeyboardTarget::modifiers(&p.window, seat, data, modifiers, serial))
}
}
impl PointerTarget<State> for CosmicWindow {
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
if let Some(sessions) = self.window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer)
}
}
PointerTarget::enter(&self.window, seat, data, event)
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
if let Some(sessions) = self.window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
if let Some((geo, hotspot)) =
seat.cursor_geometry(buffer_loc, data.common.clock.now())
{
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
if self.0.with_program(|p| {
if let Some(sessions) = p.window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer)
}
}
}
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) {
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
if p.has_ssd() {
if event.location.y < SSD_HEIGHT as f64 {
let focus = p.swap_focus(Focus::Header);
assert_eq!(focus, Focus::None);
return true;
} else {
let focus = p.swap_focus(Focus::Window);
assert_eq!(focus, Focus::None);
if let Some(sessions) = self.window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
session.cursor_leave(seat, InputType::Pointer)
let mut event = event.clone();
event.location.y -= SSD_HEIGHT as f64;
PointerTarget::enter(&p.window, seat, data, &event)
}
} else {
PointerTarget::enter(&p.window, seat, data, event)
}
false
}) {
PointerTarget::enter(&self.0, seat, data, event)
}
}
PointerTarget::leave(&self.window, seat, data, serial, time)
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
if let Some((previous, next)) = self.0.with_program(|p| {
if let Some(sessions) = p.window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
if let Some((geo, hotspot)) =
seat.cursor_geometry(buffer_loc, data.common.clock.now())
{
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
}
}
}
if p.has_ssd() {
if event.location.y < SSD_HEIGHT as f64 {
let previous = p.swap_focus(Focus::Header);
if previous == Focus::Window {
PointerTarget::leave(&p.window, seat, data, event.serial, event.time);
}
Some((previous, Focus::Header))
} else {
let mut event = event.clone();
event.location.y -= SSD_HEIGHT as f64;
let previous = p.swap_focus(Focus::Window);
if previous != Focus::Window {
PointerTarget::enter(&p.window, seat, data, &event);
} else {
PointerTarget::motion(&p.window, seat, data, &event);
}
Some((previous, Focus::Window))
}
} else {
PointerTarget::motion(&p.window, seat, data, event);
None
}
}) {
match (previous, next) {
(Focus::Header, Focus::Header) => PointerTarget::motion(&self.0, seat, data, event),
(_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, event),
(Focus::Header, _) => {
PointerTarget::leave(&self.0, seat, data, event.serial, event.time)
}
_ => {}
};
}
}
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
match self.0.with_program(|p| p.current_focus()) {
Focus::Header => PointerTarget::button(&self.0, seat, data, event),
Focus::Window => self
.0
.with_program(|p| PointerTarget::button(&p.window, seat, data, event)),
_ => {}
}
}
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
match self.0.with_program(|p| p.current_focus()) {
Focus::Header => PointerTarget::axis(&self.0, seat, data, frame),
Focus::Window => self
.0
.with_program(|p| PointerTarget::axis(&p.window, seat, data, frame)),
_ => {}
}
}
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
let previous = self.0.with_program(|p| {
if let Some(sessions) = p.window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() {
session.cursor_leave(seat, InputType::Pointer)
}
}
p.swap_focus(Focus::None)
});
assert!(previous != Focus::None);
match previous {
Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time),
Focus::Window => self
.0
.with_program(|p| PointerTarget::leave(&p.window, seat, data, serial, time)),
_ => {}
}
}
}
render_elements! {
pub CosmicWindowRenderElement<R> where R: ImportAll;
pub CosmicWindowRenderElement<R> where R: ImportAll + ImportMem;
Header=MemoryRenderBufferRenderElement<R>,
Window=WaylandSurfaceRenderElement<R>,
}
impl<R> AsRenderElements<R> for CosmicWindow
where
R: Renderer + ImportAll,
R: Renderer + ImportAll + ImportMem,
<R as Renderer>::TextureId: 'static,
{
type RenderElement = CosmicWindowRenderElement<R>;
fn render_elements<C: From<Self::RenderElement>>(
&self,
renderer: &mut R,
location: Point<i32, Physical>,
mut location: Point<i32, Physical>,
scale: Scale<f64>,
) -> Vec<C> {
AsRenderElements::<R>::render_elements::<CosmicWindowRenderElement<R>>(
&self.window,
renderer,
location,
scale,
)
.into_iter()
.map(C::from)
.collect()
let has_ssd = self.0.with_program(|p| p.has_ssd());
let mut elements = if has_ssd {
let elements = AsRenderElements::<R>::render_elements::<CosmicWindowRenderElement<R>>(
&self.0, renderer, location, scale,
);
location.y += SSD_HEIGHT;
elements
} else {
Vec::new()
};
elements.extend(self.0.with_program(|p| {
AsRenderElements::<R>::render_elements::<CosmicWindowRenderElement<R>>(
&p.window, renderer, location, scale,
)
.into_iter()
}));
elements.into_iter().map(C::from).collect()
}
}