use crate::{ state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions, }; use smithay::{ backend::{ input::KeyState, renderer::{ element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, ImportAll, Renderer, }, }, desktop::{space::SpaceElement, Window}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, Seat, }, output::Output, reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode, render_elements, utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, wayland::shell::xdg::ToplevelSurface, }; use std::{ hash::Hash, sync::{Arc, Mutex}, }; #[derive(Debug, Clone)] pub struct CosmicWindow { pub(super) window: Window, pub(super) header: Arc>>, } impl PartialEq for CosmicWindow { fn eq(&self, other: &Window) -> bool { &self.window == other } } impl PartialEq for Window { fn eq(&self, other: &CosmicWindow) -> bool { self == &other.window } } impl PartialEq for CosmicWindow { fn eq(&self, other: &Self) -> bool { self.window == other.window } } impl Eq for CosmicWindow {} impl Hash for CosmicWindow { fn hash(&self, state: &mut H) { self.window.hash(state) } } impl CosmicWindow { pub fn set_size(&self, size: Size) { 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)); } } impl From 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()))), } } } impl From 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, Default, PartialEq)] pub(super) struct HeaderBar { pointer_loc: Option>, close_button_hover: bool, maximize_button_hover: bool, } impl HeaderBar { pub fn height(&self) -> i32 { 0 } } impl IsAlive for CosmicWindow { fn alive(&self) -> bool { self.window.alive() } } impl SpaceElement for CosmicWindow { fn bbox(&self) -> Rectangle { SpaceElement::bbox(&self.window) } fn is_in_input_region(&self, point: &Point) -> bool { SpaceElement::is_in_input_region(&self.window, point) } fn set_activate(&self, activated: bool) { SpaceElement::set_activate(&self.window, activated) } fn output_enter(&self, output: &Output, overlap: Rectangle) { SpaceElement::output_enter(&self.window, output, overlap) } fn output_leave(&self, output: &Output) { SpaceElement::output_leave(&self.window, output) } fn geometry(&self) -> Rectangle { SpaceElement::geometry(&self.window) } fn z_index(&self) -> u8 { SpaceElement::z_index(&self.window) } fn refresh(&self) { SpaceElement::refresh(&self.window) } } impl KeyboardTarget for CosmicWindow { fn enter( &self, seat: &Seat, data: &mut State, keys: Vec>, serial: Serial, ) { KeyboardTarget::enter(&self.window, seat, data, keys, serial) } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { KeyboardTarget::leave(&self.window, seat, data, serial) } fn key( &self, seat: &Seat, data: &mut State, key: KeysymHandle<'_>, state: KeyState, serial: Serial, time: u32, ) { KeyboardTarget::key(&self.window, seat, data, key, state, serial, time) } fn modifiers( &self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial, ) { KeyboardTarget::modifiers(&self.window, seat, data, modifiers, serial) } } impl PointerTarget for CosmicWindow { fn enter(&self, seat: &Seat, 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::() { for session in &*sessions.0.borrow() { session.cursor_enter(seat, InputType::Pointer) } } PointerTarget::enter(&self.window, seat, data, event) } fn motion(&self, seat: &Seat, 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::() { 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.window, seat, data, event) } fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { PointerTarget::button(&self.window, seat, data, event) } fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { PointerTarget::axis(&self.window, seat, data, frame) } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; if let Some(sessions) = self.window.user_data().get::() { for session in &*sessions.0.borrow() { session.cursor_leave(seat, InputType::Pointer) } } PointerTarget::leave(&self.window, seat, data, serial, time) } } render_elements! { pub CosmicWindowRenderElement where R: ImportAll; Window=WaylandSurfaceRenderElement, } impl AsRenderElements for CosmicWindow where R: Renderer + ImportAll, ::TextureId: 'static, { type RenderElement = CosmicWindowRenderElement; fn render_elements>( &self, renderer: &mut R, location: Point, scale: Scale, ) -> Vec { AsRenderElements::::render_elements::>( &self.window, renderer, location, scale, ) .into_iter() .map(C::from) .collect() } }