2022-11-03 18:51:27 +01:00
|
|
|
use crate::{
|
2024-09-27 23:41:58 +02:00
|
|
|
backend::render::cursor::CursorState,
|
2025-09-29 17:34:30 +02:00
|
|
|
hooks::{Decorations, HOOKS},
|
2024-02-28 19:50:35 +01:00
|
|
|
shell::{
|
2024-03-25 21:48:34 +01:00
|
|
|
focus::target::PointerFocusTarget,
|
2024-02-28 19:50:35 +01:00
|
|
|
grabs::{ReleaseMode, ResizeEdge},
|
|
|
|
|
},
|
2023-01-16 15:12:25 +01:00
|
|
|
state::State,
|
|
|
|
|
utils::{
|
|
|
|
|
iced::{IcedElement, Program},
|
2023-10-25 19:24:51 +02:00
|
|
|
prelude::*,
|
2023-01-16 15:12:25 +01:00
|
|
|
},
|
2022-11-03 18:51:27 +01:00
|
|
|
};
|
2023-01-16 15:12:25 +01:00
|
|
|
use calloop::LoopHandle;
|
2024-10-24 00:15:05 +02:00
|
|
|
use cosmic::iced::{Color, Task};
|
2022-09-28 12:01:29 +02:00
|
|
|
use smithay::{
|
|
|
|
|
backend::{
|
|
|
|
|
input::KeyState,
|
|
|
|
|
renderer::{
|
2025-10-16 18:53:57 +02:00
|
|
|
ImportAll, ImportMem, Renderer,
|
2023-01-16 15:12:25 +01:00
|
|
|
element::{
|
2025-10-16 18:53:57 +02:00
|
|
|
AsRenderElements, memory::MemoryRenderBufferRenderElement,
|
|
|
|
|
surface::WaylandSurfaceRenderElement,
|
2023-01-16 15:12:25 +01:00
|
|
|
},
|
2022-09-28 12:01:29 +02:00
|
|
|
},
|
|
|
|
|
},
|
2025-10-16 18:53:57 +02:00
|
|
|
desktop::{WindowSurfaceType, space::SpaceElement},
|
2022-09-28 12:01:29 +02:00
|
|
|
input::{
|
2025-10-16 18:53:57 +02:00
|
|
|
Seat,
|
2022-09-28 12:01:29 +02:00
|
|
|
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
2023-09-05 10:55:23 -07:00
|
|
|
pointer::{
|
2024-09-09 16:21:27 +02:00
|
|
|
AxisFrame, ButtonEvent, CursorIcon, CursorImageStatus, GestureHoldBeginEvent,
|
|
|
|
|
GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent,
|
|
|
|
|
GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent,
|
|
|
|
|
GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
},
|
2024-03-26 15:20:40 -07:00
|
|
|
touch::{
|
|
|
|
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
|
|
|
|
UpEvent,
|
|
|
|
|
},
|
2022-09-28 12:01:29 +02:00
|
|
|
},
|
|
|
|
|
output::Output,
|
2023-09-18 18:29:13 +02:00
|
|
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
2023-06-08 21:50:16 +02:00
|
|
|
render_elements,
|
2024-09-27 23:41:58 +02:00
|
|
|
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
|
2023-01-18 20:23:41 +01:00
|
|
|
wayland::seat::WaylandFocus,
|
2022-09-28 12:01:29 +02:00
|
|
|
};
|
|
|
|
|
use std::{
|
2024-05-13 14:16:21 -07:00
|
|
|
borrow::Cow,
|
2023-01-16 15:12:25 +01:00
|
|
|
fmt,
|
2022-09-28 12:01:29 +02:00
|
|
|
hash::Hash,
|
2023-01-16 15:12:25 +01:00
|
|
|
sync::{
|
2025-10-30 14:54:32 -07:00
|
|
|
Mutex,
|
2025-10-16 18:53:57 +02:00
|
|
|
atomic::{AtomicBool, AtomicU8, Ordering},
|
2023-01-16 15:12:25 +01:00
|
|
|
},
|
2022-09-28 12:01:29 +02:00
|
|
|
};
|
2023-09-18 18:29:13 +02:00
|
|
|
use wayland_backend::server::ObjectId;
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2024-12-25 14:11:22 +01:00
|
|
|
use super::CosmicSurface;
|
|
|
|
|
|
|
|
|
|
pub const SSD_HEIGHT: i32 = 36;
|
|
|
|
|
pub const RESIZE_BORDER: i32 = 10;
|
2022-09-28 12:01:29 +02:00
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
2024-06-07 19:15:16 +02:00
|
|
|
pub struct CosmicWindow(pub(super) IcedElement<CosmicWindowInternal>);
|
2023-01-16 15:12:25 +01:00
|
|
|
|
|
|
|
|
impl fmt::Debug for CosmicWindow {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2023-06-13 18:32:04 +02:00
|
|
|
f.debug_struct("CosmicWindow")
|
|
|
|
|
.field("internal", &self.0)
|
|
|
|
|
.finish_non_exhaustive()
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
pub struct CosmicWindowInternal {
|
|
|
|
|
pub(super) window: CosmicSurface,
|
2025-10-30 14:54:32 -07:00
|
|
|
activated: AtomicBool,
|
2023-01-18 20:23:41 +01:00
|
|
|
/// TODO: This needs to be per seat
|
2025-10-30 14:54:32 -07:00
|
|
|
pointer_entered: AtomicU8,
|
|
|
|
|
last_title: Mutex<String>,
|
2023-01-18 20:23:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for CosmicWindowInternal {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
f.debug_struct("CosmicWindowInternal")
|
|
|
|
|
.field("window", &self.window)
|
2023-06-26 16:59:39 +02:00
|
|
|
.field("activated", &self.activated.load(Ordering::SeqCst))
|
2023-01-18 20:23:41 +01:00
|
|
|
.field("pointer_entered", &self.pointer_entered)
|
|
|
|
|
// skip seat to avoid loop
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
#[repr(u8)]
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
pub enum Focus {
|
2024-03-26 13:48:56 -07:00
|
|
|
Header = 1,
|
2024-02-28 19:50:35 +01:00
|
|
|
ResizeTop,
|
|
|
|
|
ResizeLeft,
|
|
|
|
|
ResizeRight,
|
|
|
|
|
ResizeBottom,
|
|
|
|
|
ResizeTopRight,
|
|
|
|
|
ResizeTopLeft,
|
|
|
|
|
ResizeBottomRight,
|
|
|
|
|
ResizeBottomLeft,
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-26 14:10:56 -07:00
|
|
|
impl Focus {
|
2024-03-28 11:59:33 -07:00
|
|
|
pub fn under(
|
|
|
|
|
surface: &CosmicSurface,
|
|
|
|
|
header_height: i32,
|
|
|
|
|
location: Point<f64, Logical>,
|
|
|
|
|
) -> Option<Focus> {
|
2024-03-26 14:10:56 -07:00
|
|
|
let geo = surface.geometry();
|
2024-03-28 13:36:38 -07:00
|
|
|
let loc = location.to_i32_round::<i32>() - geo.loc;
|
2024-03-26 14:10:56 -07:00
|
|
|
if loc.y < 0 && loc.x < 0 {
|
|
|
|
|
Some(Focus::ResizeTopLeft)
|
|
|
|
|
} else if loc.y < 0 && loc.x >= geo.size.w {
|
|
|
|
|
Some(Focus::ResizeTopRight)
|
|
|
|
|
} else if loc.y < 0 {
|
|
|
|
|
Some(Focus::ResizeTop)
|
2024-03-28 11:59:33 -07:00
|
|
|
} else if loc.y >= header_height + geo.size.h && loc.x < 0 {
|
2024-03-26 14:10:56 -07:00
|
|
|
Some(Focus::ResizeBottomLeft)
|
2024-03-28 11:59:33 -07:00
|
|
|
} else if loc.y >= header_height + geo.size.h && loc.x >= geo.size.w {
|
2024-03-26 14:10:56 -07:00
|
|
|
Some(Focus::ResizeBottomRight)
|
2024-03-28 11:59:33 -07:00
|
|
|
} else if loc.y >= header_height + geo.size.h {
|
2024-03-26 14:10:56 -07:00
|
|
|
Some(Focus::ResizeBottom)
|
|
|
|
|
} else if loc.x < 0 {
|
|
|
|
|
Some(Focus::ResizeLeft)
|
|
|
|
|
} else if loc.x >= geo.size.w {
|
|
|
|
|
Some(Focus::ResizeRight)
|
2024-03-28 11:59:33 -07:00
|
|
|
} else if loc.y < header_height {
|
2024-03-26 14:10:56 -07:00
|
|
|
Some(Focus::Header)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-09 16:21:27 +02:00
|
|
|
pub fn cursor_shape(&self) -> CursorIcon {
|
2024-03-26 14:10:56 -07:00
|
|
|
match self {
|
2024-09-09 16:21:27 +02:00
|
|
|
Focus::ResizeTopLeft => CursorIcon::NwResize,
|
|
|
|
|
Focus::ResizeTopRight => CursorIcon::NeResize,
|
|
|
|
|
Focus::ResizeTop => CursorIcon::NResize,
|
|
|
|
|
Focus::ResizeBottomLeft => CursorIcon::SwResize,
|
|
|
|
|
Focus::ResizeBottomRight => CursorIcon::SeResize,
|
|
|
|
|
Focus::ResizeBottom => CursorIcon::SResize,
|
|
|
|
|
Focus::ResizeLeft => CursorIcon::WResize,
|
|
|
|
|
Focus::ResizeRight => CursorIcon::EResize,
|
|
|
|
|
Focus::Header => CursorIcon::Default,
|
2024-03-26 14:10:56 -07:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-28 11:59:33 -07:00
|
|
|
|
|
|
|
|
pub unsafe fn from_u8(value: u8) -> Option<Focus> {
|
|
|
|
|
match value {
|
|
|
|
|
0 => None,
|
|
|
|
|
focus => unsafe { Some(std::mem::transmute::<u8, Focus>(focus)) },
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-26 14:10:56 -07:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
impl CosmicWindowInternal {
|
2024-03-26 13:48:56 -07:00
|
|
|
pub fn swap_focus(&self, focus: Option<Focus>) -> Option<Focus> {
|
|
|
|
|
let value = focus.map_or(0, |x| x as u8);
|
2024-03-28 11:59:33 -07:00
|
|
|
unsafe { Focus::from_u8(self.pointer_entered.swap(value, Ordering::SeqCst)) }
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-26 13:48:56 -07:00
|
|
|
pub fn current_focus(&self) -> Option<Focus> {
|
2024-03-28 11:59:33 -07:00
|
|
|
unsafe { Focus::from_u8(self.pointer_entered.load(Ordering::SeqCst)) }
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
2025-04-03 12:51:12 +02:00
|
|
|
|
2024-09-04 11:13:59 -05:00
|
|
|
/// returns if the window has any current or pending server-side decorations
|
2023-06-12 17:43:11 +02:00
|
|
|
pub fn has_ssd(&self, pending: bool) -> bool {
|
|
|
|
|
!self.window.is_decorated(pending)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2025-04-03 12:51:12 +02:00
|
|
|
|
|
|
|
|
/// returns if the window is currently or pending tiled
|
|
|
|
|
pub fn is_tiled(&self, pending: bool) -> bool {
|
|
|
|
|
self.window.is_tiled(pending).unwrap_or(false)
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CosmicWindow {
|
2023-01-16 15:12:25 +01:00
|
|
|
pub fn new(
|
|
|
|
|
window: impl Into<CosmicSurface>,
|
2023-09-29 21:33:16 +02:00
|
|
|
handle: LoopHandle<'static, crate::state::State>,
|
2023-10-10 13:55:34 -04:00
|
|
|
theme: cosmic::Theme,
|
2023-01-16 15:12:25 +01:00
|
|
|
) -> CosmicWindow {
|
|
|
|
|
let window = window.into();
|
|
|
|
|
let width = window.geometry().size.w;
|
2023-03-06 19:26:26 +01:00
|
|
|
let last_title = window.title();
|
2023-01-16 15:12:25 +01:00
|
|
|
CosmicWindow(IcedElement::new(
|
|
|
|
|
CosmicWindowInternal {
|
|
|
|
|
window,
|
2025-10-30 14:54:32 -07:00
|
|
|
activated: AtomicBool::new(false),
|
|
|
|
|
pointer_entered: AtomicU8::new(0),
|
|
|
|
|
last_title: Mutex::new(last_title),
|
2023-01-16 15:12:25 +01:00
|
|
|
},
|
|
|
|
|
(width, SSD_HEIGHT),
|
|
|
|
|
handle,
|
2023-10-10 13:55:34 -04:00
|
|
|
theme,
|
2023-01-16 15:12:25 +01:00
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 22:15:27 +02:00
|
|
|
pub fn pending_size(&self) -> Option<Size<i32, Logical>> {
|
|
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
let mut size = p.window.pending_size()?;
|
|
|
|
|
if p.has_ssd(true) {
|
|
|
|
|
size.h += SSD_HEIGHT;
|
|
|
|
|
}
|
|
|
|
|
Some(size)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:24:51 +02:00
|
|
|
pub fn set_geometry(&self, geo: Rectangle<i32, Global>) {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0.with_program(|p| {
|
2024-12-05 17:30:27 +01:00
|
|
|
let ssd_height = if p.has_ssd(true) { SSD_HEIGHT } else { 0 };
|
|
|
|
|
let loc = (geo.loc.x, geo.loc.y + ssd_height);
|
|
|
|
|
let size = (geo.size.w, std::cmp::max(geo.size.h - ssd_height, 0));
|
2023-01-23 18:25:01 +01:00
|
|
|
p.window
|
2024-12-26 18:18:35 -08:00
|
|
|
.set_geometry(Rectangle::new(loc.into(), size.into()), ssd_height as u32);
|
2023-01-16 15:12:25 +01:00
|
|
|
});
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 18:21:44 +01:00
|
|
|
pub fn on_commit(&self, surface: &WlSurface) {
|
2024-03-28 13:13:27 -07:00
|
|
|
let mut geo = None;
|
2024-03-28 18:21:44 +01:00
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
if &p.window == surface {
|
|
|
|
|
p.window.0.on_commit();
|
2024-03-28 13:13:27 -07:00
|
|
|
geo = Some(p.window.geometry());
|
2024-03-28 18:21:44 +01:00
|
|
|
}
|
2024-03-28 13:13:27 -07:00
|
|
|
});
|
|
|
|
|
if let Some(geo) = geo {
|
|
|
|
|
self.0.resize(Size::from((geo.size.w, SSD_HEIGHT)));
|
|
|
|
|
}
|
2024-03-28 18:21:44 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
pub fn surface(&self) -> CosmicSurface {
|
|
|
|
|
self.0.with_program(|p| p.window.clone())
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-25 21:48:34 +01:00
|
|
|
pub fn focus_under(
|
|
|
|
|
&self,
|
|
|
|
|
mut relative_pos: Point<f64, Logical>,
|
2024-10-10 23:18:04 +02:00
|
|
|
surface_type: WindowSurfaceType,
|
2024-06-18 19:23:16 -07:00
|
|
|
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
|
2024-03-25 21:48:34 +01:00
|
|
|
self.0.with_program(|p| {
|
2024-06-18 19:23:16 -07:00
|
|
|
let mut offset = Point::from((0., 0.));
|
2024-03-25 21:48:34 +01:00
|
|
|
let mut window_ui = None;
|
2025-04-03 12:51:12 +02:00
|
|
|
let has_ssd = p.has_ssd(false);
|
|
|
|
|
if (has_ssd || p.is_tiled(false)) && surface_type.contains(WindowSurfaceType::TOPLEVEL)
|
|
|
|
|
{
|
2024-03-25 21:48:34 +01:00
|
|
|
let geo = p.window.geometry();
|
|
|
|
|
|
|
|
|
|
let point_i32 = relative_pos.to_i32_round::<i32>();
|
2025-10-16 13:50:32 +02:00
|
|
|
let ssd_height = if has_ssd { SSD_HEIGHT } else { 0 };
|
2025-04-03 12:51:12 +02:00
|
|
|
|
2024-03-25 21:48:34 +01:00
|
|
|
if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0)
|
|
|
|
|
|| (point_i32.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0)
|
|
|
|
|
|| (point_i32.x - geo.loc.x >= geo.size.w
|
|
|
|
|
&& point_i32.x - geo.loc.x < geo.size.w + RESIZE_BORDER)
|
2025-04-03 12:51:12 +02:00
|
|
|
|| (point_i32.y - geo.loc.y >= geo.size.h + ssd_height
|
|
|
|
|
&& point_i32.y - geo.loc.y < geo.size.h + ssd_height + RESIZE_BORDER)
|
2024-03-25 21:48:34 +01:00
|
|
|
{
|
|
|
|
|
window_ui = Some((
|
|
|
|
|
PointerFocusTarget::WindowUI(self.clone()),
|
2024-06-18 19:23:16 -07:00
|
|
|
Point::from((0., 0.)),
|
2024-03-25 21:48:34 +01:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-03 12:51:12 +02:00
|
|
|
if has_ssd && (point_i32.y - geo.loc.y < SSD_HEIGHT) {
|
2024-03-25 21:48:34 +01:00
|
|
|
window_ui = Some((
|
|
|
|
|
PointerFocusTarget::WindowUI(self.clone()),
|
2024-06-18 19:23:16 -07:00
|
|
|
Point::from((0., 0.)),
|
2024-03-25 21:48:34 +01:00
|
|
|
));
|
|
|
|
|
}
|
2024-10-25 11:27:41 +02:00
|
|
|
}
|
2024-03-25 21:48:34 +01:00
|
|
|
|
2025-04-03 12:51:12 +02:00
|
|
|
if has_ssd {
|
2024-03-25 21:48:34 +01:00
|
|
|
relative_pos.y -= SSD_HEIGHT as f64;
|
2024-06-18 19:23:16 -07:00
|
|
|
offset.y += SSD_HEIGHT as f64;
|
2024-03-25 21:48:34 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-10 23:18:04 +02:00
|
|
|
window_ui.or_else(|| {
|
|
|
|
|
p.window.0.surface_under(relative_pos, surface_type).map(
|
|
|
|
|
|(surface, surface_offset)| {
|
|
|
|
|
(
|
|
|
|
|
PointerFocusTarget::WlSurface {
|
|
|
|
|
surface,
|
|
|
|
|
toplevel: Some(p.window.clone().into()),
|
|
|
|
|
},
|
|
|
|
|
(offset + surface_offset.to_f64()),
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
})
|
2024-03-25 21:48:34 +01:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 20:35:58 +04:00
|
|
|
pub fn contains_surface(&self, window: &CosmicSurface) -> bool {
|
|
|
|
|
self.0.with_program(|p| &p.window == window)
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
pub fn offset(&self) -> Point<i32, Logical> {
|
2023-06-12 17:43:11 +02:00
|
|
|
let has_ssd = self.0.with_program(|p| p.has_ssd(false));
|
2023-01-16 15:12:25 +01:00
|
|
|
if has_ssd {
|
|
|
|
|
Point::from((0, SSD_HEIGHT))
|
|
|
|
|
} else {
|
|
|
|
|
Point::from((0, 0))
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-05 17:52:47 +02:00
|
|
|
|
2023-09-29 21:33:16 +02:00
|
|
|
pub(super) fn loop_handle(&self) -> LoopHandle<'static, crate::state::State> {
|
2023-06-05 17:52:47 +02:00
|
|
|
self.0.loop_handle()
|
|
|
|
|
}
|
2023-07-13 17:19:29 +02:00
|
|
|
|
2024-09-27 23:41:58 +02:00
|
|
|
pub fn popup_render_elements<R, C>(
|
2023-07-13 17:19:29 +02:00
|
|
|
&self,
|
|
|
|
|
renderer: &mut R,
|
2024-09-27 23:41:58 +02:00
|
|
|
location: Point<i32, Physical>,
|
|
|
|
|
scale: Scale<f64>,
|
2023-07-13 17:19:29 +02:00
|
|
|
alpha: f32,
|
2024-09-27 23:41:58 +02:00
|
|
|
) -> Vec<C>
|
2023-07-13 17:19:29 +02:00
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: Send + Clone + 'static,
|
2023-07-13 17:19:29 +02:00
|
|
|
C: From<CosmicWindowRenderElement<R>>,
|
|
|
|
|
{
|
|
|
|
|
let has_ssd = self.0.with_program(|p| p.has_ssd(false));
|
|
|
|
|
|
|
|
|
|
let window_loc = if has_ssd {
|
|
|
|
|
location + Point::from((0, (SSD_HEIGHT as f64 * scale.y) as i32))
|
|
|
|
|
} else {
|
|
|
|
|
location
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-27 23:41:58 +02:00
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
p.window
|
|
|
|
|
.popup_render_elements::<R, CosmicWindowRenderElement<R>>(
|
|
|
|
|
renderer, window_loc, scale, alpha,
|
|
|
|
|
)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(C::from)
|
|
|
|
|
.collect()
|
|
|
|
|
})
|
|
|
|
|
}
|
2024-07-09 15:21:16 -07:00
|
|
|
|
2024-09-27 23:41:58 +02:00
|
|
|
pub fn render_elements<R, C>(
|
|
|
|
|
&self,
|
|
|
|
|
renderer: &mut R,
|
|
|
|
|
location: Point<i32, Physical>,
|
|
|
|
|
scale: Scale<f64>,
|
|
|
|
|
alpha: f32,
|
2025-07-22 16:39:33 +02:00
|
|
|
scanout_override: Option<bool>,
|
2024-09-27 23:41:58 +02:00
|
|
|
) -> Vec<C>
|
|
|
|
|
where
|
|
|
|
|
R: Renderer + ImportAll + ImportMem,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: Send + Clone + 'static,
|
2024-09-27 23:41:58 +02:00
|
|
|
C: From<CosmicWindowRenderElement<R>>,
|
|
|
|
|
{
|
|
|
|
|
let has_ssd = self.0.with_program(|p| p.has_ssd(false));
|
|
|
|
|
|
|
|
|
|
let window_loc = if has_ssd {
|
|
|
|
|
location + Point::from((0, (SSD_HEIGHT as f64 * scale.y) as i32))
|
|
|
|
|
} else {
|
|
|
|
|
location
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut elements = Vec::new();
|
|
|
|
|
|
|
|
|
|
elements.extend(self.0.with_program(|p| {
|
|
|
|
|
p.window.render_elements::<R, CosmicWindowRenderElement<R>>(
|
2025-07-22 16:39:33 +02:00
|
|
|
renderer,
|
|
|
|
|
window_loc,
|
|
|
|
|
scale,
|
|
|
|
|
alpha,
|
|
|
|
|
scanout_override,
|
2024-09-27 23:41:58 +02:00
|
|
|
)
|
|
|
|
|
}));
|
2023-07-13 17:19:29 +02:00
|
|
|
|
|
|
|
|
if has_ssd {
|
|
|
|
|
let ssd_loc = location
|
|
|
|
|
+ self
|
|
|
|
|
.0
|
|
|
|
|
.with_program(|p| p.window.geometry().loc)
|
|
|
|
|
.to_physical_precise_round(scale);
|
2024-09-27 23:41:58 +02:00
|
|
|
elements.extend(AsRenderElements::<R>::render_elements::<
|
|
|
|
|
CosmicWindowRenderElement<R>,
|
|
|
|
|
>(&self.0, renderer, ssd_loc, scale, alpha))
|
2023-07-13 17:19:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-09-27 23:41:58 +02:00
|
|
|
elements.into_iter().map(C::from).collect()
|
2023-07-13 17:19:29 +02:00
|
|
|
}
|
2023-10-10 13:55:34 -04:00
|
|
|
|
|
|
|
|
pub(crate) fn set_theme(&self, theme: cosmic::Theme) {
|
|
|
|
|
self.0.set_theme(theme);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn force_redraw(&self) {
|
|
|
|
|
self.0.force_redraw();
|
|
|
|
|
}
|
2024-12-25 14:11:22 +01:00
|
|
|
|
|
|
|
|
pub fn min_size(&self) -> Option<Size<i32, Logical>> {
|
|
|
|
|
self.0
|
|
|
|
|
.with_program(|p| p.window.min_size_without_ssd())
|
|
|
|
|
.map(|size| {
|
|
|
|
|
if self.0.with_program(|p| !p.window.is_decorated(false)) {
|
|
|
|
|
size + (0, SSD_HEIGHT).into()
|
|
|
|
|
} else {
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
pub fn max_size(&self) -> Option<Size<i32, Logical>> {
|
|
|
|
|
self.0
|
|
|
|
|
.with_program(|p| p.window.max_size_without_ssd())
|
|
|
|
|
.map(|size| {
|
|
|
|
|
if self.0.with_program(|p| !p.window.is_decorated(false)) {
|
|
|
|
|
size + (0, SSD_HEIGHT).into()
|
|
|
|
|
} else {
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-10-03 12:28:57 -04:00
|
|
|
|
|
|
|
|
pub fn corner_radius(&self, geometry_size: Size<i32, Logical>) -> Option<[u8; 4]> {
|
|
|
|
|
self.0
|
|
|
|
|
.with_program(|p| p.window.corner_radius(geometry_size))
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
pub enum Message {
|
|
|
|
|
DragStart,
|
2024-03-05 16:37:01 +01:00
|
|
|
Minimize,
|
2023-01-16 15:12:25 +01:00
|
|
|
Maximize,
|
|
|
|
|
Close,
|
2023-12-07 19:53:41 +00:00
|
|
|
Menu,
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
impl Program for CosmicWindowInternal {
|
|
|
|
|
type Message = Message;
|
|
|
|
|
|
2023-01-18 20:23:41 +01:00
|
|
|
fn update(
|
|
|
|
|
&mut self,
|
|
|
|
|
message: Self::Message,
|
2023-09-29 21:33:16 +02:00
|
|
|
loop_handle: &LoopHandle<'static, crate::state::State>,
|
2025-02-13 21:07:15 +01:00
|
|
|
last_seat: Option<&(Seat<State>, Serial)>,
|
2024-10-24 00:15:05 +02:00
|
|
|
) -> Task<Self::Message> {
|
2023-01-16 15:12:25 +01:00
|
|
|
match message {
|
2023-01-18 20:23:41 +01:00
|
|
|
Message::DragStart => {
|
2025-02-13 21:07:15 +01:00
|
|
|
if let Some((seat, serial)) = last_seat.cloned() {
|
2024-05-13 14:16:21 -07:00
|
|
|
if let Some(surface) = self.window.wl_surface().map(Cow::into_owned) {
|
2023-09-29 21:33:16 +02:00
|
|
|
loop_handle.insert_idle(move |state| {
|
2025-05-20 17:41:27 +02:00
|
|
|
let res = state.common.shell.write().move_request(
|
2023-12-07 19:49:53 +00:00
|
|
|
&surface,
|
|
|
|
|
&seat,
|
|
|
|
|
serial,
|
|
|
|
|
ReleaseMode::NoMouseButtons,
|
2023-12-20 20:29:44 +00:00
|
|
|
false,
|
2024-04-10 15:49:08 +02:00
|
|
|
&state.common.config,
|
|
|
|
|
&state.common.event_loop_handle,
|
2024-08-19 16:01:04 +01:00
|
|
|
false,
|
2023-12-07 19:49:53 +00:00
|
|
|
);
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some((grab, focus)) = res {
|
|
|
|
|
if grab.is_touch_grab() {
|
|
|
|
|
seat.get_touch().unwrap().set_grab(state, grab, serial);
|
|
|
|
|
} else {
|
|
|
|
|
seat.get_pointer()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.set_grab(state, grab, serial, focus);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-18 20:23:41 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-05 16:37:01 +01:00
|
|
|
Message::Minimize => {
|
2024-05-13 14:16:21 -07:00
|
|
|
if let Some(surface) = self.window.wl_surface().map(Cow::into_owned) {
|
2024-03-05 16:37:01 +01:00
|
|
|
loop_handle.insert_idle(move |state| {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = state.common.shell.write();
|
2025-06-25 17:54:27 +02:00
|
|
|
shell.minimize_request(&surface)
|
2024-03-05 16:37:01 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-18 20:23:41 +01:00
|
|
|
Message::Maximize => {
|
2024-05-13 14:16:21 -07:00
|
|
|
if let Some(surface) = self.window.wl_surface().map(Cow::into_owned) {
|
2023-10-25 19:41:30 +02:00
|
|
|
loop_handle.insert_idle(move |state| {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = state.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(&surface).cloned() {
|
|
|
|
|
let seat = shell.seats.last_active().clone();
|
2025-06-25 17:54:27 +02:00
|
|
|
shell.maximize_toggle(&mapped, &seat, &state.common.event_loop_handle)
|
2023-10-25 19:41:30 +02:00
|
|
|
}
|
|
|
|
|
});
|
2023-01-18 20:23:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Message::Close => self.window.close(),
|
2023-12-07 19:53:41 +00:00
|
|
|
Message::Menu => {
|
2025-02-13 21:07:15 +01:00
|
|
|
if let Some((seat, serial)) = last_seat.cloned() {
|
2024-05-13 14:16:21 -07:00
|
|
|
if let Some(surface) = self.window.wl_surface().map(Cow::into_owned) {
|
2023-12-07 19:53:41 +00:00
|
|
|
loop_handle.insert_idle(move |state| {
|
2025-05-20 17:41:27 +02:00
|
|
|
let shell = state.common.shell.read();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(&surface).cloned() {
|
2023-12-20 20:19:42 +00:00
|
|
|
let position = if let Some((output, set)) =
|
2024-04-10 15:49:08 +02:00
|
|
|
shell.workspaces.sets.iter().find(|(_, set)| {
|
2023-12-20 20:19:42 +00:00
|
|
|
set.sticky_layer.mapped().any(|m| m == &mapped)
|
|
|
|
|
}) {
|
|
|
|
|
set.sticky_layer
|
2023-12-07 19:53:41 +00:00
|
|
|
.element_geometry(&mapped)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.loc
|
2023-12-20 20:19:42 +00:00
|
|
|
.to_global(output)
|
2024-04-10 15:49:08 +02:00
|
|
|
} else if let Some(workspace) = shell.space_for(&mapped) {
|
2024-02-23 17:25:40 +01:00
|
|
|
let Some(elem_geo) = workspace.element_geometry(&mapped) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
elem_geo.loc.to_global(&workspace.output)
|
2023-12-20 20:19:42 +00:00
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-10 15:49:08 +02:00
|
|
|
let pointer = seat.get_pointer().unwrap();
|
|
|
|
|
let mut cursor = pointer.current_location().to_i32_round();
|
2023-12-20 20:51:04 +00:00
|
|
|
cursor.y -= SSD_HEIGHT;
|
2024-04-10 15:49:08 +02:00
|
|
|
|
|
|
|
|
let res = shell.menu_request(
|
2023-12-20 20:51:04 +00:00
|
|
|
&surface,
|
|
|
|
|
&seat,
|
|
|
|
|
serial,
|
|
|
|
|
cursor - position.as_logical(),
|
|
|
|
|
false,
|
2024-04-10 15:49:08 +02:00
|
|
|
&state.common.config,
|
|
|
|
|
&state.common.event_loop_handle,
|
2023-12-20 20:51:04 +00:00
|
|
|
);
|
2024-04-10 15:49:08 +02:00
|
|
|
|
|
|
|
|
std::mem::drop(shell);
|
|
|
|
|
if let Some((grab, focus)) = res {
|
|
|
|
|
pointer.set_grab(state, grab, serial, focus);
|
|
|
|
|
}
|
2023-12-07 19:53:41 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
2024-10-24 00:15:05 +02:00
|
|
|
Task::none()
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-02 20:40:59 +02:00
|
|
|
fn background_color(&self, theme: &cosmic::Theme) -> Color {
|
2024-06-28 11:24:54 +02:00
|
|
|
if self.window.is_maximized(false) {
|
2024-08-02 20:40:59 +02:00
|
|
|
theme.cosmic().background.base.into()
|
|
|
|
|
} else {
|
|
|
|
|
Color::TRANSPARENT
|
2023-05-31 20:02:39 +02:00
|
|
|
}
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-07 22:20:44 +01:00
|
|
|
fn view(&self) -> cosmic::Element<'_, Self::Message> {
|
2025-09-29 17:34:30 +02:00
|
|
|
HOOKS.get().unwrap().window_decorations.view(self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct DefaultDecorations;
|
|
|
|
|
|
|
|
|
|
impl Decorations<CosmicWindowInternal, Message> for DefaultDecorations {
|
|
|
|
|
fn view(&self, win: &CosmicWindowInternal) -> cosmic::Element<'_, Message> {
|
2024-07-22 14:21:56 +02:00
|
|
|
let mut header = cosmic::widget::header_bar()
|
2025-09-29 17:34:30 +02:00
|
|
|
.title(win.last_title.lock().unwrap().clone())
|
2023-01-16 15:12:25 +01:00
|
|
|
.on_drag(Message::DragStart)
|
|
|
|
|
.on_close(Message::Close)
|
2025-09-29 17:34:30 +02:00
|
|
|
.focused(win.window.is_activated(false))
|
2024-09-19 19:25:25 +02:00
|
|
|
.on_double_click(Message::Maximize)
|
2025-05-19 12:21:28 -07:00
|
|
|
.on_right_click(Message::Menu)
|
|
|
|
|
.is_ssd(true);
|
2024-07-22 14:21:56 +02:00
|
|
|
|
|
|
|
|
if cosmic::config::show_minimize() {
|
2025-05-19 12:21:28 -07:00
|
|
|
header = header.on_minimize(Message::Minimize)
|
2024-07-22 14:21:56 +02:00
|
|
|
}
|
|
|
|
|
if cosmic::config::show_maximize() {
|
2025-05-19 12:21:28 -07:00
|
|
|
header = header.on_maximize(Message::Maximize)
|
2024-07-22 14:21:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header.into()
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IsAlive for CosmicWindow {
|
|
|
|
|
fn alive(&self) -> bool {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0.with_program(|p| p.window.alive())
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SpaceElement for CosmicWindow {
|
|
|
|
|
fn bbox(&self) -> Rectangle<i32, Logical> {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
let mut bbox = SpaceElement::bbox(&p.window);
|
2025-04-03 12:51:12 +02:00
|
|
|
let has_ssd = p.has_ssd(false);
|
|
|
|
|
|
|
|
|
|
if has_ssd || p.is_tiled(false) {
|
2024-02-28 19:50:35 +01:00
|
|
|
bbox.loc -= Point::from((RESIZE_BORDER, RESIZE_BORDER));
|
|
|
|
|
bbox.size += Size::from((RESIZE_BORDER * 2, RESIZE_BORDER * 2));
|
2025-04-03 12:51:12 +02:00
|
|
|
}
|
|
|
|
|
if has_ssd {
|
2023-01-16 15:12:25 +01:00
|
|
|
bbox.size.h += SSD_HEIGHT;
|
|
|
|
|
}
|
2025-04-03 12:51:12 +02:00
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
bbox
|
|
|
|
|
})
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
2024-10-10 23:18:04 +02:00
|
|
|
self.focus_under(*point, WindowSurfaceType::ALL).is_some()
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
fn set_activate(&self, activated: bool) {
|
2023-06-26 16:59:39 +02:00
|
|
|
if self
|
|
|
|
|
.0
|
|
|
|
|
.with_program(|p| p.activated.load(Ordering::SeqCst) != activated)
|
|
|
|
|
{
|
|
|
|
|
SpaceElement::set_activate(&self.0, activated);
|
|
|
|
|
self.0.force_redraw();
|
|
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
p.activated.store(activated, Ordering::SeqCst);
|
|
|
|
|
SpaceElement::set_activate(&p.window, activated);
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-10-07 19:15:44 -07:00
|
|
|
#[profiling::function]
|
2022-09-28 12:01:29 +02:00
|
|
|
fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
|
2023-01-16 15:12:25 +01:00
|
|
|
SpaceElement::output_enter(&self.0, output, overlap);
|
|
|
|
|
self.0
|
|
|
|
|
.with_program(|p| SpaceElement::output_enter(&p.window, output, overlap));
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-10-07 19:15:44 -07:00
|
|
|
#[profiling::function]
|
2022-09-28 12:01:29 +02:00
|
|
|
fn output_leave(&self, output: &Output) {
|
2023-01-16 15:12:25 +01:00
|
|
|
SpaceElement::output_leave(&self.0, output);
|
|
|
|
|
self.0
|
|
|
|
|
.with_program(|p| SpaceElement::output_leave(&p.window, output));
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
fn geometry(&self) -> Rectangle<i32, Logical> {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
let mut geo = SpaceElement::geometry(&p.window);
|
2023-06-12 17:43:11 +02:00
|
|
|
if p.has_ssd(false) {
|
2023-01-16 15:12:25 +01:00
|
|
|
geo.size.h += SSD_HEIGHT;
|
|
|
|
|
}
|
|
|
|
|
geo
|
|
|
|
|
})
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
fn z_index(&self) -> u8 {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0.with_program(|p| SpaceElement::z_index(&p.window))
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-10-07 19:15:44 -07:00
|
|
|
#[profiling::function]
|
2022-09-28 12:01:29 +02:00
|
|
|
fn refresh(&self) {
|
2023-03-06 19:26:26 +01:00
|
|
|
if self.0.with_program(|p| {
|
|
|
|
|
SpaceElement::refresh(&p.window);
|
2025-04-24 19:00:01 +02:00
|
|
|
if !p.has_ssd(true) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 19:26:26 +01:00
|
|
|
let title = p.window.title();
|
|
|
|
|
let mut last_title = p.last_title.lock().unwrap();
|
|
|
|
|
if *last_title != title {
|
|
|
|
|
*last_title = title;
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}) {
|
|
|
|
|
self.0.force_update();
|
2025-04-24 19:00:01 +02:00
|
|
|
} else {
|
|
|
|
|
SpaceElement::refresh(&self.0);
|
2023-03-06 19:26:26 +01:00
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl KeyboardTarget<State> for CosmicWindow {
|
|
|
|
|
fn enter(
|
|
|
|
|
&self,
|
|
|
|
|
seat: &Seat<State>,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
keys: Vec<KeysymHandle<'_>>,
|
|
|
|
|
serial: Serial,
|
|
|
|
|
) {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0
|
|
|
|
|
.with_program(|p| KeyboardTarget::enter(&p.window, seat, data, keys, serial))
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0
|
|
|
|
|
.with_program(|p| KeyboardTarget::leave(&p.window, 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,
|
|
|
|
|
) {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0
|
|
|
|
|
.with_program(|p| KeyboardTarget::key(&p.window, 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,
|
|
|
|
|
) {
|
2023-01-16 15:12:25 +01:00
|
|
|
self.0
|
|
|
|
|
.with_program(|p| KeyboardTarget::modifiers(&p.window, seat, data, modifiers, serial))
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PointerTarget<State> for CosmicWindow {
|
|
|
|
|
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
2024-02-28 19:50:35 +01:00
|
|
|
let mut event = event.clone();
|
2024-03-25 21:48:34 +01:00
|
|
|
self.0.with_program(|p| {
|
2025-04-03 12:51:12 +02:00
|
|
|
let has_ssd = p.has_ssd(false);
|
|
|
|
|
if has_ssd || p.is_tiled(false) {
|
|
|
|
|
let Some(next) = Focus::under(
|
|
|
|
|
&p.window,
|
2025-10-16 13:50:32 +02:00
|
|
|
if has_ssd { SSD_HEIGHT } else { 0 },
|
2025-04-03 12:51:12 +02:00
|
|
|
event.location,
|
|
|
|
|
) else {
|
2024-03-25 21:48:34 +01:00
|
|
|
return;
|
2024-02-28 19:50:35 +01:00
|
|
|
};
|
2025-04-03 12:51:12 +02:00
|
|
|
|
2024-03-26 13:48:56 -07:00
|
|
|
let old_focus = p.swap_focus(Some(next));
|
|
|
|
|
assert_eq!(old_focus, None);
|
2024-02-28 19:50:35 +01:00
|
|
|
|
|
|
|
|
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
2024-06-07 19:26:23 +02:00
|
|
|
cursor_state.lock().unwrap().set_shape(next.cursor_shape());
|
2025-06-06 15:33:58 -07:00
|
|
|
seat.set_cursor_image_status(CursorImageStatus::default_named());
|
2022-11-03 18:51:27 +01:00
|
|
|
}
|
2024-03-25 21:48:34 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64());
|
|
|
|
|
PointerTarget::enter(&self.0, seat, data, &event)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-01-16 15:12:25 +01:00
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
2024-02-28 19:50:35 +01:00
|
|
|
let mut event = event.clone();
|
2024-03-25 21:48:34 +01:00
|
|
|
self.0.with_program(|p| {
|
2025-04-03 12:51:12 +02:00
|
|
|
let has_ssd = p.has_ssd(false);
|
|
|
|
|
if has_ssd || p.is_tiled(false) {
|
|
|
|
|
let Some(next) = Focus::under(
|
|
|
|
|
&p.window,
|
2025-10-16 13:50:32 +02:00
|
|
|
if has_ssd { SSD_HEIGHT } else { 0 },
|
2025-04-03 12:51:12 +02:00
|
|
|
event.location,
|
|
|
|
|
) else {
|
2024-03-25 21:48:34 +01:00
|
|
|
return;
|
2024-02-28 19:50:35 +01:00
|
|
|
};
|
2024-03-26 13:48:56 -07:00
|
|
|
let _previous = p.swap_focus(Some(next));
|
2024-02-28 19:50:35 +01:00
|
|
|
|
|
|
|
|
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
2024-06-07 19:26:23 +02:00
|
|
|
cursor_state.lock().unwrap().set_shape(next.cursor_shape());
|
2025-06-06 15:33:58 -07:00
|
|
|
seat.set_cursor_image_status(CursorImageStatus::default_named());
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
2024-03-25 21:48:34 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64());
|
|
|
|
|
PointerTarget::motion(&self.0, seat, data, &event)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-01-16 15:12:25 +01:00
|
|
|
|
2024-03-25 21:48:34 +01:00
|
|
|
fn relative_motion(
|
|
|
|
|
&self,
|
|
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &RelativeMotionEvent,
|
|
|
|
|
) {
|
2023-01-30 23:19:36 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
2023-01-16 15:12:25 +01:00
|
|
|
match self.0.with_program(|p| p.current_focus()) {
|
2025-02-13 21:07:15 +01:00
|
|
|
Some(Focus::Header) => PointerTarget::button(&self.0, seat, data, event),
|
2024-03-26 13:48:56 -07:00
|
|
|
Some(x) => {
|
2024-02-28 19:50:35 +01:00
|
|
|
let serial = event.serial;
|
|
|
|
|
let seat = seat.clone();
|
2024-05-13 14:16:21 -07:00
|
|
|
let Some(surface) = self.wl_surface().map(Cow::into_owned) else {
|
2024-02-28 19:50:35 +01:00
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
self.0.loop_handle().insert_idle(move |state| {
|
2025-05-20 17:41:27 +02:00
|
|
|
let res = state.common.shell.write().resize_request(
|
2024-02-28 19:50:35 +01:00
|
|
|
&surface,
|
|
|
|
|
&seat,
|
|
|
|
|
serial,
|
|
|
|
|
match x {
|
|
|
|
|
Focus::ResizeTop => ResizeEdge::TOP,
|
|
|
|
|
Focus::ResizeTopLeft => ResizeEdge::TOP_LEFT,
|
|
|
|
|
Focus::ResizeTopRight => ResizeEdge::TOP_RIGHT,
|
|
|
|
|
Focus::ResizeBottom => ResizeEdge::BOTTOM,
|
|
|
|
|
Focus::ResizeBottomLeft => ResizeEdge::BOTTOM_LEFT,
|
|
|
|
|
Focus::ResizeBottomRight => ResizeEdge::BOTTOM_RIGHT,
|
|
|
|
|
Focus::ResizeLeft => ResizeEdge::LEFT,
|
|
|
|
|
Focus::ResizeRight => ResizeEdge::RIGHT,
|
2024-03-26 13:48:56 -07:00
|
|
|
Focus::Header => unreachable!(),
|
2024-02-28 19:50:35 +01:00
|
|
|
},
|
2025-02-14 21:58:09 +11:00
|
|
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
2024-08-19 16:01:04 +01:00
|
|
|
false,
|
2024-04-10 15:49:08 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if let Some((grab, focus)) = res {
|
|
|
|
|
if grab.is_touch_grab() {
|
|
|
|
|
seat.get_touch().unwrap().set_grab(state, grab, serial);
|
|
|
|
|
} else {
|
|
|
|
|
seat.get_pointer()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.set_grab(state, grab, serial, focus);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-28 19:50:35 +01:00
|
|
|
});
|
|
|
|
|
}
|
2024-03-26 13:48:56 -07:00
|
|
|
None => {}
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-01-16 15:12:25 +01:00
|
|
|
|
2022-09-28 12:01:29 +02:00
|
|
|
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
2025-10-16 16:10:35 +02:00
|
|
|
if let Some(Focus::Header) = self.0.with_program(|p| p.current_focus()) {
|
|
|
|
|
PointerTarget::axis(&self.0, seat, data, frame)
|
|
|
|
|
}
|
2023-09-13 20:52:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn frame(&self, seat: &Seat<State>, data: &mut State) {
|
2025-10-16 16:10:35 +02:00
|
|
|
if let Some(Focus::Header) = self.0.with_program(|p| p.current_focus()) {
|
|
|
|
|
PointerTarget::frame(&self.0, seat, data)
|
|
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2022-11-03 18:51:27 +01:00
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
2024-03-25 21:48:34 +01:00
|
|
|
self.0.with_program(|p| {
|
2024-02-28 19:50:35 +01:00
|
|
|
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
2024-09-09 16:21:27 +02:00
|
|
|
cursor_state.lock().unwrap().unset_shape();
|
2024-03-26 13:48:56 -07:00
|
|
|
let _previous = p.swap_focus(None);
|
2023-01-16 15:12:25 +01:00
|
|
|
});
|
2024-03-25 21:48:34 +01:00
|
|
|
PointerTarget::leave(&self.0, seat, data, serial, time)
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
2023-09-05 10:55:23 -07:00
|
|
|
|
|
|
|
|
fn gesture_swipe_begin(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GestureSwipeBeginEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_swipe_update(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GestureSwipeUpdateEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_swipe_end(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GestureSwipeEndEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_pinch_begin(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GesturePinchBeginEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_pinch_update(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GesturePinchUpdateEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_pinch_end(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GesturePinchEndEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_hold_begin(
|
|
|
|
|
&self,
|
2024-03-25 21:48:34 +01:00
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GestureHoldBeginEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-25 21:48:34 +01:00
|
|
|
fn gesture_hold_end(
|
|
|
|
|
&self,
|
|
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &GestureHoldEndEvent,
|
|
|
|
|
) {
|
2023-09-05 10:55:23 -07:00
|
|
|
}
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-26 15:20:40 -07:00
|
|
|
impl TouchTarget<State> for CosmicWindow {
|
|
|
|
|
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
|
|
|
|
let mut event = event.clone();
|
2024-04-18 21:19:05 -07:00
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
event.location -= p.window.geometry().loc.to_f64();
|
|
|
|
|
});
|
2024-03-26 15:20:40 -07:00
|
|
|
TouchTarget::down(&self.0, seat, data, &event, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
2025-10-16 13:50:32 +02:00
|
|
|
TouchTarget::up(&self.0, seat, data, event, seq)
|
2024-03-26 15:20:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
|
|
|
|
|
let mut event = event.clone();
|
|
|
|
|
event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64());
|
|
|
|
|
TouchTarget::motion(&self.0, seat, data, &event, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
|
|
|
|
TouchTarget::frame(&self.0, seat, data, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
|
|
|
|
TouchTarget::cancel(&self.0, seat, data, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
|
|
|
|
|
TouchTarget::shape(&self.0, seat, data, event, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn orientation(
|
|
|
|
|
&self,
|
|
|
|
|
_seat: &Seat<State>,
|
|
|
|
|
_data: &mut State,
|
|
|
|
|
_event: &OrientationEvent,
|
|
|
|
|
_seq: Serial,
|
|
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 18:29:13 +02:00
|
|
|
impl WaylandFocus for CosmicWindow {
|
2024-05-13 14:16:21 -07:00
|
|
|
fn wl_surface(&self) -> Option<Cow<'_, WlSurface>> {
|
|
|
|
|
self.0.with_program(|p| {
|
|
|
|
|
p.window
|
|
|
|
|
.wl_surface()
|
|
|
|
|
.map(|s| Cow::Owned(Cow::into_owned(s)))
|
|
|
|
|
})
|
2023-09-18 18:29:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn same_client_as(&self, object_id: &ObjectId) -> bool {
|
|
|
|
|
self.0.with_program(|p| p.window.same_client_as(object_id))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-08 21:50:16 +02:00
|
|
|
render_elements! {
|
|
|
|
|
pub CosmicWindowRenderElement<R> where R: ImportAll + ImportMem;
|
|
|
|
|
Header = MemoryRenderBufferRenderElement<R>,
|
|
|
|
|
Window = WaylandSurfaceRenderElement<R>,
|
2022-09-28 12:01:29 +02:00
|
|
|
}
|