cosmic-comp/src/shell/element/stack.rs

743 lines
24 KiB
Rust
Raw Normal View History

2022-11-03 18:51:27 +01:00
use crate::{
2023-06-05 17:52:47 +02:00
shell::focus::FocusDirection,
state::State,
utils::iced::{IcedElement, Program},
utils::prelude::SeatExt,
wayland::handlers::screencopy::ScreencopySessions,
2022-11-03 18:51:27 +01:00
};
use calloop::LoopHandle;
use cosmic::{iced_core::Color, Element};
2022-11-03 18:51:27 +01:00
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
2022-09-28 12:01:29 +02:00
use smithay::{
backend::{
input::KeyState,
renderer::{
element::{
memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement,
AsRenderElements,
},
ImportAll, ImportMem, Renderer,
2022-09-28 12:01:29 +02:00
},
},
desktop::space::SpaceElement,
2022-09-28 12:01:29 +02:00
input::{
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent},
2022-09-28 12:01:29 +02:00
Seat,
},
output::Output,
render_elements,
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
};
use std::{
fmt,
2022-09-28 12:01:29 +02:00
hash::Hash,
sync::{
atomic::{AtomicU8, AtomicUsize, Ordering},
2022-09-28 12:01:29 +02:00
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()
})
}
}
2022-09-28 12:01:29 +02:00
#[derive(Debug, Clone)]
pub struct CosmicStackInternal {
windows: Arc<Mutex<Vec<CosmicSurface>>>,
2022-09-28 12:01:29 +02:00
active: Arc<AtomicUsize>,
2022-11-03 18:51:27 +01:00
previous_keyboard: Arc<AtomicUsize>,
pointer_entered: Option<Arc<AtomicU8>>,
2022-11-03 18:51:27 +01:00
previous_pointer: Arc<AtomicUsize>,
last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>,
2023-05-31 20:02:39 +02:00
mask: Arc<Mutex<Option<tiny_skia::Mask>>>,
2022-09-28 12:01:29 +02:00
}
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
}
2022-09-28 12:01:29 +02:00
}
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
}
2022-09-28 12:01:29 +02:00
}
}
const TAB_HEIGHT: i32 = 24;
2022-09-28 12:01:29 +02:00
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Focus {
None,
Header,
Window,
2022-09-28 12:01:29 +02:00
}
impl CosmicStack {
pub fn new<I: Into<CosmicSurface>>(
windows: impl Iterator<Item = I>,
handle: LoopHandle<'static, crate::state::Data>,
) -> CosmicStack {
let windows = windows.map(Into::into).collect::<Vec<_>>();
assert!(!windows.is_empty());
let width = windows[0].geometry().size.w;
CosmicStack(IcedElement::new(
CosmicStackInternal {
windows: Arc::new(Mutex::new(windows)),
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)),
2023-05-31 20:02:39 +02:00
mask: Arc::new(Mutex::new(None)),
},
(width, TAB_HEIGHT),
handle,
))
2022-09-28 12:01:29 +02:00
}
pub fn add_window(&self, window: CosmicSurface) {
self.0
.with_program(|p| p.windows.lock().unwrap().push(window));
}
pub fn remove_window(&self, window: &CosmicSurface) {
self.0.with_program(|p| {
let mut windows = p.windows.lock().unwrap();
2023-06-05 17:52:47 +02:00
if windows.len() == 1 {
return;
}
let Some(idx) = windows.iter().position(|w| w == window) else { return };
windows.remove(idx);
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
})
}
pub fn remove_idx(&self, idx: usize) {
self.0.with_program(|p| {
let mut windows = p.windows.lock().unwrap();
2023-06-05 17:52:47 +02:00
if windows.len() == 1 {
return;
}
if windows.len() >= idx {
return;
}
windows.remove(idx);
2023-06-05 17:52:47 +02:00
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
})
}
pub fn len(&self) -> usize {
self.0.with_program(|p| p.windows.lock().unwrap().len())
}
2023-06-05 17:52:47 +02:00
pub fn handle_focus(&self, direction: FocusDirection) -> bool {
self.0.with_program(|p| {
match direction {
FocusDirection::Left => p
.active
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| val.checked_sub(1))
.is_ok(),
FocusDirection::Right => {
let max = p.windows.lock().unwrap().len();
p.active
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
if val < max - 1 {
Some(val + 1)
} else {
None
}
})
.is_ok()
}
FocusDirection::Out => false, //TODO
FocusDirection::In => false, //TODO
_ => false,
}
})
}
pub fn active(&self) -> CosmicSurface {
self.0
.with_program(|p| p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].clone())
}
2023-04-01 20:35:58 +04:00
pub fn has_active(&self, window: &CosmicSurface) -> bool {
self.0
.with_program(|p| &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] == window)
}
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))
2022-09-28 12:01:29 +02:00
}
2023-01-23 18:25:01 +01:00
pub fn set_geometry(&self, geo: Rectangle<i32, Logical>) {
self.0.with_program(|p| {
2023-01-23 18:25:01 +01:00
let loc = (geo.loc.x, geo.loc.y + TAB_HEIGHT);
let size = (geo.size.w, geo.size.h - TAB_HEIGHT);
for window in p.windows.lock().unwrap().iter() {
2023-01-23 18:25:01 +01:00
window.set_geometry(Rectangle::from_loc_and_size(loc, size));
}
});
2023-01-23 18:25:01 +01:00
self.0.resize(Size::from((geo.size.w, TAB_HEIGHT)));
2022-09-28 12:01:29 +02:00
}
2022-11-03 18:51:27 +01:00
fn keyboard_leave_if_previous(
&self,
seat: &Seat<State>,
data: &mut State,
serial: Serial,
) -> usize {
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
})
2022-11-03 18:51:27 +01:00
}
fn pointer_leave_if_previous(
&self,
seat: &Seat<State>,
data: &mut State,
serial: Serial,
time: u32,
location: Point<f64, Logical>,
) -> usize {
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)
}
2022-11-03 18:51:27 +01:00
}
PointerTarget::leave(
&p.windows.lock().unwrap()[previous],
seat,
data,
2022-11-03 18:51:27 +01:00
serial,
time,
);
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
})
}
2023-06-05 17:52:47 +02:00
pub(super) fn loop_handle(&self) -> LoopHandle<'static, crate::state::Data> {
self.0.loop_handle()
}
}
impl Program for CosmicStackInternal {
type Message = ();
fn view(&self) -> Element<'_, Self::Message> {
cosmic::iced::widget::text("TODO").into()
2022-11-03 18:51:27 +01:00
}
2023-05-31 20:02:39 +02:00
fn clip_mask(&self, size: Size<i32, smithay::utils::Buffer>, scale: f32) -> tiny_skia::Mask {
let mut mask = self.mask.lock().unwrap();
if mask.is_none() {
let mut new_mask = tiny_skia::Mask::new(size.w as u32, size.h as u32).unwrap();
let mut pb = tiny_skia::PathBuilder::new();
let radius = 8. * scale;
let (w, h) = (size.w as f32, size.h as f32);
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().unwrap();
new_mask.fill_path(
&path,
tiny_skia::FillRule::EvenOdd,
true,
Default::default(),
);
*mask = Some(new_mask);
}
mask.clone().unwrap()
}
fn background_color(&self) -> Color {
Color {
2023-05-31 20:02:39 +02:00
r: 0.1176,
g: 0.1176,
b: 0.1176,
a: 1.0,
}
}
2022-09-28 12:01:29 +02:00
}
impl IsAlive for CosmicStack {
fn alive(&self) -> bool {
self.0
.with_program(|p| p.windows.lock().unwrap().iter().any(IsAlive::alive))
2022-09-28 12:01:29 +02:00
}
}
impl SpaceElement for CosmicStack {
fn bbox(&self) -> Rectangle<i32, Logical> {
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
})
2022-09-28 12:01:29 +02:00
}
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
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,
)
})
2022-09-28 12:01:29 +02:00
}
fn set_activate(&self, activated: bool) {
SpaceElement::set_activate(&self.0, activated);
self.0.with_program(|p| {
p.windows
.lock()
.unwrap()
.iter()
.for_each(|w| SpaceElement::set_activate(w, activated))
})
2022-09-28 12:01:29 +02:00
}
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
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))
})
2022-09-28 12:01:29 +02:00
}
fn output_leave(&self, output: &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))
})
2022-09-28 12:01:29 +02:00
}
fn geometry(&self) -> Rectangle<i32, Logical> {
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
})
2022-09-28 12:01:29 +02:00
}
fn z_index(&self) -> u8 {
self.0.with_program(|p| {
SpaceElement::z_index(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)])
})
2022-09-28 12:01:29 +02:00
}
fn refresh(&self) {
self.0.with_program(|p| {
let mut windows = p.windows.lock().unwrap();
2023-06-05 17:52:47 +02:00
// don't let the stack become empty
let active = windows[p.active.load(Ordering::SeqCst)].clone();
windows.retain(IsAlive::alive);
if windows.is_empty() {
windows.push(active);
}
let len = windows.len();
let _ = p
.active
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| {
2023-06-05 17:52:47 +02:00
(active >= len).then_some(len - 1)
});
windows.iter().for_each(|w| SpaceElement::refresh(w))
})
2022-09-28 12:01:29 +02:00
}
}
impl KeyboardTarget<State> for CosmicStack {
fn enter(
&self,
seat: &Seat<State>,
data: &mut State,
keys: Vec<KeysymHandle<'_>>,
serial: 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,
)
})
2022-09-28 12:01:29 +02:00
}
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
2022-11-03 18:51:27 +01:00
let active = self.keyboard_leave_if_previous(seat, data, serial);
self.0.with_program(|p| {
KeyboardTarget::leave(&p.windows.lock().unwrap()[active], seat, data, serial)
})
2022-09-28 12:01:29 +02:00
}
fn key(
&self,
seat: &Seat<State>,
data: &mut State,
key: KeysymHandle<'_>,
state: KeyState,
serial: Serial,
time: u32,
) {
2022-11-03 18:51:27 +01:00
let active = self.keyboard_leave_if_previous(seat, data, serial);
self.0.with_program(|p| {
KeyboardTarget::key(
&p.windows.lock().unwrap()[active],
seat,
data,
key,
state,
serial,
time,
)
})
2022-09-28 12:01:29 +02:00
}
fn modifiers(
&self,
seat: &Seat<State>,
data: &mut State,
modifiers: ModifiersState,
serial: Serial,
) {
2022-11-03 18:51:27 +01:00
let active = self.keyboard_leave_if_previous(seat, data, serial);
self.0.with_program(|p| {
KeyboardTarget::modifiers(
&p.windows.lock().unwrap()[active],
seat,
data,
modifiers,
serial,
)
})
2022-09-28 12:01:29 +02:00
}
}
impl PointerTarget<State> for CosmicStack {
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
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)
}
2022-11-03 18:51:27 +01:00
}
if event.location.y < TAB_HEIGHT as f64 {
let focus = p.swap_focus(Focus::Header);
true
} else {
let focus = p.swap_focus(Focus::Window);
2022-11-03 18:51:27 +01:00
*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)
}
2022-09-28 12:01:29 +02:00
}
2022-09-28 12:01:29 +02:00
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
2022-11-03 18:51:27 +01:00
let active =
self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location);
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);
}
}
}
2022-11-03 18:51:27 +01:00
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);
2022-11-03 18:51:27 +01:00
}
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)
}
_ => {}
2022-11-03 18:51:27 +01:00
}
}
2022-09-28 12:01:29 +02:00
}
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
self.0.with_program(|p| {
if p.current_focus() == Focus::Window {
let window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)];
window.relative_motion(seat, data, event)
}
})
}
2022-09-28 12:01:29 +02:00
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
if let Some((location, _serial, _time)) = self
.0
.with_program(|p| p.last_location.lock().unwrap().clone())
{
2022-11-03 18:51:27 +01:00
self.pointer_leave_if_previous(seat, data, event.serial, event.time, location);
}
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,
)
}),
_ => {}
}
2022-09-28 12:01:29 +02:00
}
2022-09-28 12:01:29 +02:00
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
if let Some((location, serial, time)) = self
.0
.with_program(|p| p.last_location.lock().unwrap().clone())
{
2022-11-03 18:51:27 +01:00
self.pointer_leave_if_previous(seat, data, serial, time, location);
}
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,
)
}),
_ => {}
}
2022-09-28 12:01:29 +02:00
}
2022-09-28 12:01:29 +02:00
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
if let Some((location, serial, time)) = self
.0
.with_program(|p| p.last_location.lock().unwrap().clone())
{
2022-11-03 18:51:27 +01:00
self.pointer_leave_if_previous(seat, data, serial, time, location);
}
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)
}
2022-11-03 18:51:27 +01:00
}
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,
)
}),
_ => {}
}
2022-09-28 12:01:29 +02:00
}
}
render_elements! {
pub CosmicStackRenderElement<R> where R: ImportAll + ImportMem;
Header=MemoryRenderBufferRenderElement<R>,
Window=WaylandSurfaceRenderElement<R>,
2022-09-28 12:01:29 +02:00
}
impl<R> AsRenderElements<R> for CosmicStack
where
R: Renderer + ImportAll + ImportMem,
2022-09-28 12:01:29 +02:00
<R as Renderer>::TextureId: 'static,
{
type RenderElement = CosmicStackRenderElement<R>;
fn render_elements<C: From<Self::RenderElement>>(
&self,
renderer: &mut R,
mut location: Point<i32, Physical>,
2022-09-28 12:01:29 +02:00
scale: Scale<f64>,
2023-05-12 20:01:37 +02:00
alpha: f32,
2022-09-28 12:01:29 +02:00
) -> Vec<C> {
let mut elements = AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
2023-05-12 20:01:37 +02:00
&self.0, renderer, location, scale, alpha,
);
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,
2023-05-12 20:01:37 +02:00
alpha,
);
elements
}));
elements.into_iter().map(C::from).collect()
2022-09-28 12:01:29 +02:00
}
}