update smithay, iced; reworked window for xwayland
This commit is contained in:
parent
47dfc85314
commit
7992ad67f6
27 changed files with 2285 additions and 1106 deletions
|
|
@ -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>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
579
src/shell/element/surface.rs
Normal file
579
src/shell/element/surface.rs
Normal 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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use std::sync::Weak;
|
||||
|
||||
use crate::{shell::element::CosmicMapped, utils::prelude::*};
|
||||
use crate::{
|
||||
shell::{element::CosmicMapped, CosmicSurface},
|
||||
utils::prelude::*,
|
||||
};
|
||||
use id_tree::NodeId;
|
||||
use smithay::{
|
||||
backend::input::KeyState,
|
||||
desktop::{LayerSurface, PopupKind, Window},
|
||||
desktop::{LayerSurface, PopupKind},
|
||||
input::{
|
||||
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget},
|
||||
|
|
@ -19,7 +22,7 @@ use smithay::{
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PointerFocusTarget {
|
||||
Element(CosmicMapped),
|
||||
Fullscreen(Window),
|
||||
Fullscreen(CosmicSurface),
|
||||
LayerSurface(LayerSurface),
|
||||
Popup(PopupKind),
|
||||
}
|
||||
|
|
@ -27,7 +30,7 @@ pub enum PointerFocusTarget {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum KeyboardFocusTarget {
|
||||
Element(CosmicMapped),
|
||||
Fullscreen(Window),
|
||||
Fullscreen(CosmicSurface),
|
||||
Group(WindowGroup),
|
||||
LayerSurface(LayerSurface),
|
||||
Popup(PopupKind),
|
||||
|
|
@ -263,9 +266,9 @@ impl From<CosmicMapped> for PointerFocusTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Window> for PointerFocusTarget {
|
||||
fn from(w: Window) -> Self {
|
||||
PointerFocusTarget::Fullscreen(w)
|
||||
impl From<CosmicSurface> for PointerFocusTarget {
|
||||
fn from(s: CosmicSurface) -> Self {
|
||||
PointerFocusTarget::Fullscreen(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,9 +290,9 @@ impl From<CosmicMapped> for KeyboardFocusTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Window> for KeyboardFocusTarget {
|
||||
fn from(w: Window) -> Self {
|
||||
KeyboardFocusTarget::Fullscreen(w)
|
||||
impl From<CosmicSurface> for KeyboardFocusTarget {
|
||||
fn from(s: CosmicSurface) -> Self {
|
||||
KeyboardFocusTarget::Fullscreen(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{AsRenderElements, RenderElement},
|
||||
ImportAll, Renderer,
|
||||
ImportAll, ImportMem, Renderer,
|
||||
},
|
||||
desktop::space::SpaceElement,
|
||||
input::{
|
||||
|
|
@ -38,7 +38,7 @@ pub struct MoveGrabState {
|
|||
impl MoveGrabState {
|
||||
pub fn render<I, R>(&self, renderer: &mut R, seat: &Seat<State>, output: &Output) -> Vec<I>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
I: From<CosmicMappedRenderElement<R>>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
shell::{element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge},
|
||||
shell::{
|
||||
element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge, CosmicSurface,
|
||||
},
|
||||
utils::prelude::*,
|
||||
};
|
||||
use smithay::{
|
||||
|
|
@ -84,18 +86,10 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
|
|||
|
||||
let (min_size, max_size) = (self.window.min_size(), self.window.max_size());
|
||||
|
||||
let min_width = min_size.w.max(1);
|
||||
let min_height = min_size.h.max(1);
|
||||
let max_width = if max_size.w == 0 {
|
||||
i32::max_value()
|
||||
} else {
|
||||
max_size.w
|
||||
};
|
||||
let max_height = if max_size.h == 0 {
|
||||
i32::max_value()
|
||||
} else {
|
||||
max_size.h
|
||||
};
|
||||
let min_width = min_size.map(|s| s.w).unwrap_or(1);
|
||||
let min_height = min_size.map(|s| s.h).unwrap_or(1);
|
||||
let max_width = max_size.map(|s| s.w).unwrap_or(i32::max_value());
|
||||
let max_height = max_size.map(|s| s.h).unwrap_or(i32::max_value());
|
||||
|
||||
new_window_width = new_window_width.max(min_width).min(max_width);
|
||||
new_window_height = new_window_height.max(min_height).min(max_height);
|
||||
|
|
@ -210,7 +204,7 @@ impl ResizeSurfaceGrab {
|
|||
|
||||
// Finish resizing.
|
||||
if let Some(ResizeState::WaitingForCommit(_)) = *resize_state {
|
||||
if !window.is_resizing() {
|
||||
if !window.is_resizing().unwrap_or(false) {
|
||||
*resize_state = None;
|
||||
}
|
||||
}
|
||||
|
|
@ -218,11 +212,13 @@ impl ResizeSurfaceGrab {
|
|||
|
||||
if let Some(new_location) = new_location {
|
||||
for (window, offset) in window.windows() {
|
||||
update_reactive_popups(
|
||||
&window,
|
||||
new_location + offset,
|
||||
space.floating_layer.space.outputs(),
|
||||
);
|
||||
if let CosmicSurface::Wayland(window) = window {
|
||||
update_reactive_popups(
|
||||
&window,
|
||||
new_location + offset,
|
||||
space.floating_layer.space.outputs(),
|
||||
);
|
||||
}
|
||||
}
|
||||
space
|
||||
.floating_layer
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::{element::RenderElement, ImportAll, Renderer},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, Space, Window},
|
||||
backend::renderer::{element::RenderElement, ImportAll, ImportMem, Renderer},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, Space},
|
||||
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
|
||||
output::Output,
|
||||
utils::{Logical, Point, Rectangle, Serial},
|
||||
utils::{Logical, Point, Rectangle, Serial, Size},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
shell::{
|
||||
element::{CosmicMapped, CosmicMappedRenderElement},
|
||||
grabs::ResizeEdge,
|
||||
OutputNotMapped,
|
||||
CosmicSurface, OutputNotMapped,
|
||||
},
|
||||
state::State,
|
||||
utils::prelude::*,
|
||||
|
|
@ -81,7 +81,10 @@ impl FloatingLayout {
|
|||
win_geo.size = size;
|
||||
}
|
||||
{
|
||||
let (min_size, max_size) = (mapped.min_size(), mapped.max_size());
|
||||
let (min_size, max_size) = (
|
||||
mapped.min_size().unwrap_or((0, 0).into()),
|
||||
mapped.max_size().unwrap_or((0, 0).into()),
|
||||
);
|
||||
if win_geo.size.w > geometry.size.w / 3 * 2 {
|
||||
// try a more reasonable size
|
||||
let mut width = geometry.size.w / 3 * 2;
|
||||
|
|
@ -155,7 +158,7 @@ impl FloatingLayout {
|
|||
self.space.element_geometry(elem)
|
||||
}
|
||||
|
||||
pub fn maximize_request(&mut self, window: &Window) {
|
||||
pub fn maximize_request(&mut self, window: &CosmicSurface) {
|
||||
if let Some(mapped) = self
|
||||
.space
|
||||
.elements()
|
||||
|
|
@ -170,7 +173,7 @@ impl FloatingLayout {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn unmaximize_request(&mut self, window: &Window) {
|
||||
pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
|
||||
let maybe_mapped = self
|
||||
.space
|
||||
.elements()
|
||||
|
|
@ -179,9 +182,13 @@ impl FloatingLayout {
|
|||
|
||||
if let Some(mapped) = maybe_mapped {
|
||||
let last_geometry = mapped.last_geometry.lock().unwrap().clone();
|
||||
mapped.set_size(last_geometry.map(|g| g.size).expect("No previous size?"));
|
||||
let last_size = last_geometry.map(|g| g.size).expect("No previous size?");
|
||||
mapped.set_size(last_size);
|
||||
let last_location = last_geometry.map(|g| g.loc).expect("No previous location?");
|
||||
self.space.map_element(mapped, last_location, true);
|
||||
Some(last_size)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +220,7 @@ impl FloatingLayout {
|
|||
self.space.elements()
|
||||
}
|
||||
|
||||
pub fn windows(&self) -> impl Iterator<Item = Window> + '_ {
|
||||
pub fn windows(&self) -> impl Iterator<Item = CosmicSurface> + '_ {
|
||||
self.mapped().flat_map(|e| e.windows().map(|(w, _)| w))
|
||||
}
|
||||
|
||||
|
|
@ -298,7 +305,7 @@ impl FloatingLayout {
|
|||
output: &Output,
|
||||
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
use regex::RegexSet;
|
||||
use smithay::{
|
||||
desktop::Window,
|
||||
wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceRoleAttributes},
|
||||
wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceData},
|
||||
xwayland::xwm::WmWindowType,
|
||||
};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::CosmicSurface;
|
||||
|
||||
pub mod floating;
|
||||
pub mod tiling;
|
||||
|
|
@ -95,33 +96,52 @@ lazy_static::lazy_static! {
|
|||
]).unwrap();
|
||||
}
|
||||
|
||||
pub fn should_be_floating(window: &Window) -> bool {
|
||||
let surface = window.toplevel().wl_surface();
|
||||
with_states(surface, |states| {
|
||||
let attrs = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
// simple heuristic taken from
|
||||
// sway/desktop/xdg_shell.c:188 @ 0ee54a52
|
||||
if attrs.parent.is_some()
|
||||
|| (attrs.min_size.w != 0 && attrs.min_size.h != 0 && attrs.min_size == attrs.max_size)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// else take a look at our exceptions
|
||||
let appid_matches = EXCEPTIONS_APPID.matches(attrs.app_id.as_deref().unwrap_or(""));
|
||||
let title_matches = EXCEPTIONS_TITLE.matches(attrs.app_id.as_deref().unwrap_or(""));
|
||||
for idx in appid_matches.into_iter() {
|
||||
if title_matches.matched(idx) {
|
||||
pub fn should_be_floating(window: &CosmicSurface) -> bool {
|
||||
// Check "window type"
|
||||
match window {
|
||||
CosmicSurface::Wayland(window) => {
|
||||
if with_states(window.toplevel().wl_surface(), |states| {
|
||||
let attrs = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
attrs.parent.is_some()
|
||||
}) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CosmicSurface::X11(surface) => {
|
||||
if surface.is_override_redirect()
|
||||
|| surface.is_popup()
|
||||
|| !matches!(
|
||||
surface.window_type(),
|
||||
None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility)
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
false
|
||||
})
|
||||
// Check if sizing suggest dialog
|
||||
let max_size = window.max_size();
|
||||
let min_size = window.min_size();
|
||||
|
||||
if min_size.is_some() && min_size == max_size {
|
||||
return true;
|
||||
}
|
||||
|
||||
// else take a look at our exceptions
|
||||
let appid_matches = EXCEPTIONS_APPID.matches(&window.app_id());
|
||||
let title_matches = EXCEPTIONS_TITLE.matches(&window.title());
|
||||
for idx in appid_matches.into_iter() {
|
||||
if title_matches.matched(idx) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
},
|
||||
grabs::ResizeEdge,
|
||||
layout::Orientation,
|
||||
OutputNotMapped,
|
||||
CosmicSurface, OutputNotMapped,
|
||||
},
|
||||
utils::prelude::*,
|
||||
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
|
||||
|
|
@ -20,12 +20,13 @@ use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBeh
|
|||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{AsRenderElements, RenderElement},
|
||||
ImportAll, Renderer,
|
||||
ImportAll, ImportMem, Renderer,
|
||||
},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Window},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, PopupKind},
|
||||
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
|
||||
output::Output,
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial},
|
||||
wayland::seat::WaylandFocus,
|
||||
};
|
||||
use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc};
|
||||
|
||||
|
|
@ -1003,7 +1004,7 @@ impl TilingLayout {
|
|||
.find(|node| match node.data() {
|
||||
Data::Mapped { mapped, .. } => mapped
|
||||
.windows()
|
||||
.any(|(w, _)| w.toplevel().wl_surface() == &toplevel_surface),
|
||||
.any(|(w, _)| w.wl_surface().as_ref() == Some(&toplevel_surface)),
|
||||
_ => false,
|
||||
})?;
|
||||
|
||||
|
|
@ -1200,7 +1201,9 @@ impl TilingLayout {
|
|||
.flatten()
|
||||
}
|
||||
|
||||
pub fn windows(&self) -> impl Iterator<Item = (Output, Window, Point<i32, Logical>)> + '_ {
|
||||
pub fn windows(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (Output, CosmicSurface, Point<i32, Logical>)> + '_ {
|
||||
self.mapped().flat_map(|(output, mapped, loc)| {
|
||||
mapped
|
||||
.windows()
|
||||
|
|
@ -1265,7 +1268,7 @@ impl TilingLayout {
|
|||
output: &Output,
|
||||
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashMap};
|
|||
|
||||
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState;
|
||||
use smithay::{
|
||||
desktop::{layer_map_for_output, LayerSurface, PopupManager, Window, WindowSurfaceType},
|
||||
desktop::{layer_map_for_output, LayerSurface, PopupManager, WindowSurfaceType},
|
||||
input::Seat,
|
||||
output::Output,
|
||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle},
|
||||
|
|
@ -37,7 +37,7 @@ pub mod focus;
|
|||
pub mod grabs;
|
||||
pub mod layout;
|
||||
mod workspace;
|
||||
pub use self::element::CosmicMappedRenderElement;
|
||||
pub use self::element::{CosmicMappedRenderElement, CosmicSurface};
|
||||
pub use self::workspace::*;
|
||||
use self::{
|
||||
element::{CosmicMapped, CosmicWindow},
|
||||
|
|
@ -50,12 +50,12 @@ pub struct Shell {
|
|||
pub outputs: Vec<Output>,
|
||||
pub workspaces: WorkspaceMode,
|
||||
pub floating_default: bool,
|
||||
pub pending_windows: Vec<(Window, Seat<State>)>,
|
||||
pub pending_windows: Vec<(CosmicSurface, Seat<State>)>,
|
||||
pub pending_layers: Vec<(LayerSurface, Output, Seat<State>)>,
|
||||
|
||||
// wayland_state
|
||||
pub layer_shell_state: WlrLayerShellState,
|
||||
pub toplevel_info_state: ToplevelInfoState<State>,
|
||||
pub toplevel_info_state: ToplevelInfoState<State, CosmicSurface>,
|
||||
pub toplevel_management_state: ToplevelManagementState,
|
||||
pub xdg_shell_state: XdgShellState,
|
||||
pub workspace_state: WorkspaceState<State>,
|
||||
|
|
@ -144,7 +144,7 @@ impl WorkspaceSet {
|
|||
fn refresh<'a>(
|
||||
&mut self,
|
||||
state: &mut WorkspaceState<State>,
|
||||
toplevel_info: &mut ToplevelInfoState<State>,
|
||||
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||
outputs: impl Iterator<Item = (&'a Output, Point<i32, Logical>)>,
|
||||
) {
|
||||
match self.amount {
|
||||
|
|
@ -213,7 +213,7 @@ impl WorkspaceSet {
|
|||
&mut self,
|
||||
amount: usize,
|
||||
state: &mut WorkspaceState<State>,
|
||||
toplevel_info: &mut ToplevelInfoState<State>,
|
||||
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||
outputs: impl Iterator<Item = (&'a Output, Point<i32, Logical>)>,
|
||||
) {
|
||||
if amount < self.workspaces.len() {
|
||||
|
|
@ -957,7 +957,7 @@ impl Shell {
|
|||
.refresh(Some(&self.workspace_state));
|
||||
}
|
||||
|
||||
pub fn map_window(state: &mut State, window: &Window, output: &Output) {
|
||||
pub fn map_window(state: &mut State, window: &CosmicSurface, output: &Output) {
|
||||
let pos = state
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -979,7 +979,10 @@ impl Shell {
|
|||
.toplevel_info_state
|
||||
.toplevel_enter_workspace(&window, &workspace.handle);
|
||||
|
||||
let mapped = CosmicMapped::from(CosmicWindow::from(window.clone()));
|
||||
let mapped = CosmicMapped::from(CosmicWindow::new(
|
||||
window.clone(),
|
||||
state.common.event_loop_handle.clone(),
|
||||
));
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
mapped.set_debug(state.common.egui.active);
|
||||
|
|
@ -1111,12 +1114,14 @@ impl Shell {
|
|||
if let Some(workspace) = self.space_for(mapped) {
|
||||
let element_loc = workspace.element_geometry(mapped).unwrap().loc;
|
||||
for (toplevel, offset) in mapped.windows() {
|
||||
let window_geo_offset = toplevel.geometry().loc;
|
||||
update_reactive_popups(
|
||||
&toplevel,
|
||||
element_loc + offset + window_geo_offset,
|
||||
self.outputs.iter(),
|
||||
);
|
||||
if let CosmicSurface::Wayland(toplevel) = toplevel {
|
||||
let window_geo_offset = toplevel.geometry().loc;
|
||||
update_reactive_popups(
|
||||
&toplevel,
|
||||
element_loc + offset + window_geo_offset,
|
||||
self.outputs.iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,19 @@ use indexmap::IndexSet;
|
|||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Element, RenderElement},
|
||||
ImportAll, Renderer,
|
||||
ImportAll, ImportMem, Renderer,
|
||||
},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, LayerSurface, Window},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, LayerSurface},
|
||||
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
|
||||
output::Output,
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial},
|
||||
wayland::shell::wlr_layer::Layer,
|
||||
utils::{
|
||||
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
|
||||
},
|
||||
wayland::{seat::WaylandFocus, shell::wlr_layer::Layer},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
|
@ -37,7 +39,7 @@ use super::{
|
|||
element::CosmicMapped,
|
||||
focus::{FocusStack, FocusStackMut},
|
||||
grabs::ResizeGrab,
|
||||
CosmicMappedRenderElement,
|
||||
CosmicMappedRenderElement, CosmicSurface,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -45,7 +47,7 @@ pub struct Workspace {
|
|||
pub tiling_layer: TilingLayout,
|
||||
pub floating_layer: FloatingLayout,
|
||||
pub tiling_enabled: bool,
|
||||
pub fullscreen: HashMap<Output, Window>,
|
||||
pub fullscreen: HashMap<Output, CosmicSurface>,
|
||||
pub handle: WorkspaceHandle,
|
||||
pub focus_stack: FocusStacks,
|
||||
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
|
||||
|
|
@ -85,7 +87,7 @@ impl Workspace {
|
|||
if let Some(mapped) = self.element_for_surface(surface) {
|
||||
mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.toplevel().wl_surface() == surface)
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface))
|
||||
.unwrap()
|
||||
.0
|
||||
.on_commit();
|
||||
|
|
@ -131,7 +133,7 @@ impl Workspace {
|
|||
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
|
||||
.find(|e| {
|
||||
e.windows()
|
||||
.any(|(w, _)| w.toplevel().wl_surface() == surface)
|
||||
.any(|(w, _)| w.wl_surface().as_ref() == Some(surface))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -190,41 +192,37 @@ impl Workspace {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn maximize_request(&mut self, window: &Window, output: &Output) {
|
||||
pub fn maximize_request(&mut self, window: &CosmicSurface, output: &Output) {
|
||||
if self.fullscreen.contains_key(output) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.floating_layer.maximize_request(window);
|
||||
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Maximized);
|
||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||
});
|
||||
|
||||
window.set_fullscreen(false);
|
||||
window.set_maximized(true);
|
||||
self.set_fullscreen(window, output)
|
||||
}
|
||||
pub fn unmaximize_request(&mut self, window: &Window) {
|
||||
pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
|
||||
if self.fullscreen.values().any(|w| w == window) {
|
||||
self.unfullscreen_request(window);
|
||||
self.floating_layer.unmaximize_request(window);
|
||||
self.floating_layer.unmaximize_request(window)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fullscreen_request(&mut self, window: &Window, output: &Output) {
|
||||
pub fn fullscreen_request(&mut self, window: &CosmicSurface, output: &Output) {
|
||||
if self.fullscreen.contains_key(output) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||
state.states.unset(xdg_toplevel::State::Maximized);
|
||||
});
|
||||
|
||||
window.set_maximized(false);
|
||||
window.set_fullscreen(true);
|
||||
self.set_fullscreen(window, output)
|
||||
}
|
||||
|
||||
fn set_fullscreen(&mut self, window: &Window, output: &Output) {
|
||||
fn set_fullscreen(&mut self, window: &CosmicSurface, output: &Output) {
|
||||
if let Some(mapped) = self
|
||||
.mapped()
|
||||
.find(|m| m.windows().any(|(w, _)| &w == window))
|
||||
|
|
@ -232,46 +230,39 @@ impl Workspace {
|
|||
mapped.set_active(window);
|
||||
}
|
||||
|
||||
let xdg = window.toplevel();
|
||||
xdg.with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
output
|
||||
.current_mode()
|
||||
.map(|m| m.size)
|
||||
.unwrap_or((0, 0).into())
|
||||
.to_f64()
|
||||
.to_logical(output.current_scale().fractional_scale())
|
||||
.to_i32_round(),
|
||||
);
|
||||
});
|
||||
xdg.send_configure();
|
||||
window.set_size(
|
||||
output
|
||||
.current_mode()
|
||||
.map(|m| m.size)
|
||||
.unwrap_or((0, 0).into())
|
||||
.to_f64()
|
||||
.to_logical(output.current_scale().fractional_scale())
|
||||
.to_i32_round(),
|
||||
);
|
||||
window.send_configure();
|
||||
self.fullscreen.insert(output.clone(), window.clone());
|
||||
}
|
||||
|
||||
pub fn unfullscreen_request(&mut self, window: &Window) {
|
||||
pub fn unfullscreen_request(&mut self, window: &CosmicSurface) {
|
||||
if self.fullscreen.values().any(|w| w == window) {
|
||||
let xdg = window.toplevel();
|
||||
xdg.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||
state.states.unset(xdg_toplevel::State::Maximized);
|
||||
state.size = None;
|
||||
});
|
||||
window.set_maximized(false);
|
||||
window.set_fullscreen(false);
|
||||
self.floating_layer.refresh();
|
||||
self.tiling_layer.refresh();
|
||||
xdg.send_configure();
|
||||
window.send_configure();
|
||||
self.fullscreen.retain(|_, w| w != window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maximize_toggle(&mut self, window: &Window, output: &Output) {
|
||||
pub fn maximize_toggle(&mut self, window: &CosmicSurface, output: &Output) {
|
||||
if self.fullscreen.contains_key(output) {
|
||||
self.unmaximize_request(window)
|
||||
self.unmaximize_request(window);
|
||||
} else {
|
||||
self.maximize_request(window, output)
|
||||
self.maximize_request(window, output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fullscreen(&self, output: &Output) -> Option<&Window> {
|
||||
pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicSurface> {
|
||||
self.fullscreen.get(output).filter(|w| w.alive())
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +294,7 @@ impl Workspace {
|
|||
|
||||
pub fn move_request(
|
||||
&mut self,
|
||||
window: &Window,
|
||||
window: &CosmicSurface,
|
||||
seat: &Seat<State>,
|
||||
output: &Output,
|
||||
_serial: Serial,
|
||||
|
|
@ -312,15 +303,12 @@ impl Workspace {
|
|||
let pointer = seat.get_pointer().unwrap();
|
||||
let pos = pointer.current_location();
|
||||
|
||||
let mapped = self
|
||||
.element_for_surface(window.toplevel().wl_surface())?
|
||||
.clone();
|
||||
let mapped = self.element_for_surface(&window.wl_surface()?)?.clone();
|
||||
let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc;
|
||||
|
||||
if mapped.is_fullscreen() || mapped.is_maximized() {
|
||||
// If surface is maximized then unmaximize it
|
||||
self.unmaximize_request(window);
|
||||
let new_size = window.toplevel().with_pending_state(|state| state.size);
|
||||
let new_size = self.unmaximize_request(window);
|
||||
let ratio = pos.x / output.geometry().size.w as f64;
|
||||
|
||||
initial_window_location = new_size
|
||||
|
|
@ -396,7 +384,7 @@ impl Workspace {
|
|||
.chain(self.tiling_layer.mapped().map(|(_, w, _)| w))
|
||||
}
|
||||
|
||||
pub fn windows(&self) -> impl Iterator<Item = Window> + '_ {
|
||||
pub fn windows(&self) -> impl Iterator<Item = CosmicSurface> + '_ {
|
||||
self.floating_layer
|
||||
.windows()
|
||||
.chain(self.tiling_layer.windows().map(|(_, w, _)| w))
|
||||
|
|
@ -408,7 +396,7 @@ impl Workspace {
|
|||
output: &Output,
|
||||
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
|
|
@ -534,7 +522,7 @@ pub struct OutputNotMapped;
|
|||
|
||||
pub enum WorkspaceRenderElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
Wayland(WaylandSurfaceRenderElement<R>),
|
||||
|
|
@ -543,7 +531,7 @@ where
|
|||
|
||||
impl<R> Element for WorkspaceRenderElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
||||
|
|
@ -609,7 +597,7 @@ where
|
|||
|
||||
impl<R> RenderElement<R> for WorkspaceRenderElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
|
|
@ -640,7 +628,7 @@ where
|
|||
|
||||
impl<R> From<WaylandSurfaceRenderElement<R>> for WorkspaceRenderElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
|
|
@ -651,7 +639,7 @@ where
|
|||
|
||||
impl<R> From<CosmicMappedRenderElement<R>> for WorkspaceRenderElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGlowRenderer,
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue