diff --git a/Cargo.lock b/Cargo.lock index 60bae03e..a634231c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,6 +477,7 @@ name = "cosmic-comp" version = "0.1.0" dependencies = [ "anyhow", + "apply", "bitflags 1.3.2", "bytemuck", "calloop", diff --git a/Cargo.toml b/Cargo.toml index 73a1ba88..167b854a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ license = "GPL-3.0-only" authors = ["Victoria Brekenfeld"] [dependencies] +apply = "0.3.0" anyhow = { version = "1.0.51", features = ["backtrace"] } bitflags = "1.3.2" bytemuck = "1.12" diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 129d33c3..7931ad39 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -11,7 +11,7 @@ use std::{ use crate::{ config::WorkspaceLayout, shell::{ - element::window::CosmicWindowRenderElement, + element::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement}, focus::target::WindowGroup, layout::{floating::SeatMoveGrabState, tiling::ANIMATION_DURATION}, CosmicMapped, CosmicMappedRenderElement, WorkspaceRenderElement, @@ -364,6 +364,7 @@ where ::TextureId: Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, E: From> + From>, { #[cfg(feature = "debug")] @@ -434,6 +435,7 @@ where ::Error: From, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { #[cfg(feature = "debug")] @@ -740,6 +742,7 @@ where CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, Source: Clone, { @@ -799,6 +802,7 @@ where CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, Source: Clone, { diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 18807eb8..39ba4c7c 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -53,7 +53,7 @@ pub mod stack; pub use self::stack::CosmicStack; pub mod window; pub use self::window::CosmicWindow; -use self::window::CosmicWindowRenderElement; +use self::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement}; #[cfg(feature = "debug")] use egui::plot::{Corner, Legend, Plot, PlotPoints, Polygon}; @@ -323,14 +323,10 @@ impl CosmicMapped { } pub fn set_activated(&self, activated: bool) { - for window in match &self.element { - CosmicMappedInternal::Stack(s) => { - Box::new(s.surfaces()) as Box> - } - CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())), + match &self.element { + CosmicMappedInternal::Stack(s) => s.set_activate(activated), + CosmicMappedInternal::Window(w) => w.set_activate(activated), _ => unreachable!(), - } { - window.set_activated(activated) } } @@ -1009,6 +1005,7 @@ where ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, { type RenderElement = CosmicMappedRenderElement; fn render_elements>( diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 1c0e9964..cc962992 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1,12 +1,22 @@ use crate::{ + backend::render::{ + element::{AsGlowFrame, AsGlowRenderer}, + GlMultiError, GlMultiFrame, GlMultiRenderer, + }, shell::focus::FocusDirection, state::State, utils::iced::{IcedElement, Program}, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions, }; +use apply::Apply; use calloop::LoopHandle; -use cosmic::{iced_core::Color, Element}; +use cosmic::{ + iced::widget as iced_widget, + iced_core::{alignment, Color, Length}, + iced_widget::rule::FillMode, + theme, widget as cosmic_widget, Element as CosmicElement, +}; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; use smithay::{ backend::{ @@ -14,8 +24,10 @@ use smithay::{ renderer::{ element::{ memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement, - AsRenderElements, + AsRenderElements, Element, Id, RenderElement, }, + glow::GlowRenderer, + utils::CommitCounter, ImportAll, ImportMem, Renderer, }, }, @@ -26,14 +38,13 @@ use smithay::{ Seat, }, output::Output, - render_elements, - utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform}, }; use std::{ fmt, hash::Hash, sync::{ - atomic::{AtomicU8, AtomicUsize, Ordering}, + atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}, Arc, Mutex, }, }; @@ -57,10 +68,13 @@ impl fmt::Debug for CosmicStack { pub struct CosmicStackInternal { windows: Arc>>, active: Arc, + activated: Arc, + group_focused: Arc, previous_keyboard: Arc, pointer_entered: Option>, previous_pointer: Arc, last_location: Arc, Serial, u32)>>>, + geometry: Arc>>>, mask: Arc>>, } @@ -103,15 +117,24 @@ impl CosmicStack { ) -> CosmicStack { let windows = windows.map(Into::into).collect::>(); assert!(!windows.is_empty()); + + for window in &windows { + window.try_force_undecorated(true); + window.set_tiled(true); + } + let width = windows[0].geometry().size.w; CosmicStack(IcedElement::new( CosmicStackInternal { windows: Arc::new(Mutex::new(windows)), active: Arc::new(AtomicUsize::new(0)), + activated: Arc::new(AtomicBool::new(false)), + group_focused: Arc::new(AtomicBool::new(false)), previous_keyboard: Arc::new(AtomicUsize::new(0)), pointer_entered: None, previous_pointer: Arc::new(AtomicUsize::new(0)), last_location: Arc::new(Mutex::new(None)), + geometry: Arc::new(Mutex::new(None)), mask: Arc::new(Mutex::new(None)), }, (width, TAB_HEIGHT), @@ -119,9 +142,13 @@ impl CosmicStack { )) } - pub fn add_window(&self, window: CosmicSurface) { + pub fn add_window(&self, window: impl Into) { + let window = window.into(); + window.try_force_undecorated(true); + window.set_tiled(true); self.0 .with_program(|p| p.windows.lock().unwrap().push(window)); + self.0.force_redraw() } pub fn remove_window(&self, window: &CosmicSurface) { @@ -132,9 +159,13 @@ impl CosmicStack { } let Some(idx) = windows.iter().position(|w| w == window) else { return }; - windows.remove(idx); + let window = windows.remove(idx); + window.try_force_undecorated(false); + window.set_tiled(false); + p.active.fetch_min(windows.len() - 1, Ordering::SeqCst); - }) + }); + self.0.force_redraw() } pub fn remove_idx(&self, idx: usize) { @@ -146,9 +177,13 @@ impl CosmicStack { if windows.len() >= idx { return; } - windows.remove(idx); + let window = windows.remove(idx); + window.try_force_undecorated(false); + window.set_tiled(false); + p.active.fetch_min(windows.len() - 1, Ordering::SeqCst); - }) + }); + self.0.force_redraw() } pub fn len(&self) -> usize { @@ -156,13 +191,18 @@ impl CosmicStack { } 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 result = self.0.with_program(|p| match direction { + FocusDirection::Left => { + if !p.group_focused.load(Ordering::SeqCst) { + p.active + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| val.checked_sub(1)) + .is_ok() + } else { + false + } + } + FocusDirection::Right => { + if !p.group_focused.load(Ordering::SeqCst) { let max = p.windows.lock().unwrap().len(); p.active .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| { @@ -173,12 +213,40 @@ impl CosmicStack { } }) .is_ok() + } else { + false } - FocusDirection::Out => false, //TODO - FocusDirection::In => false, //TODO - _ => false, } - }) + FocusDirection::Out => { + if !p.group_focused.swap(true, Ordering::SeqCst) { + p.windows.lock().unwrap().iter().for_each(|w| { + w.set_activated(false); + w.send_configure(); + }); + true + } else { + false + } + } + FocusDirection::In => { + if !p.group_focused.swap(false, Ordering::SeqCst) { + p.windows.lock().unwrap().iter().for_each(|w| { + w.set_activated(true); + w.send_configure(); + }); + true + } else { + false + } + } + _ => false, + }); + + if result { + self.0.force_update(); + } + + result } pub fn active(&self) -> CosmicSurface { @@ -198,7 +266,8 @@ impl CosmicStack { p.previous_keyboard.store(old, Ordering::SeqCst); p.previous_pointer.store(old, Ordering::SeqCst); } - }) + }); + self.0.force_redraw() } pub fn surfaces(&self) -> impl Iterator { @@ -222,9 +291,13 @@ impl CosmicStack { let loc = (geo.loc.x, geo.loc.y + TAB_HEIGHT); let size = (geo.size.w, geo.size.h - TAB_HEIGHT); + let win_geo = Rectangle::from_loc_and_size(loc, size); for window in p.windows.lock().unwrap().iter() { - window.set_geometry(Rectangle::from_loc_and_size(loc, size)); + window.set_geometry(win_geo); } + + *p.geometry.lock().unwrap() = Some(geo); + p.mask.lock().unwrap().take(); }); self.0.resize(Size::from((geo.size.w, TAB_HEIGHT))); } @@ -239,14 +312,13 @@ impl CosmicStack { 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, - ) + let windows = p.windows.lock().unwrap(); + if let Some(previous) = windows.get(previous) { + KeyboardTarget::leave(previous, seat, data, serial); + } + seat.get_keyboard().unwrap().with_pressed_keysyms(|syms| { + KeyboardTarget::enter(&windows[active], seat, data, syms, serial) + }) } active }) @@ -264,31 +336,23 @@ impl CosmicStack { 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::() - { - for session in &*sessions.0.borrow() { - session.cursor_leave(seat, InputType::Pointer) + let windows = p.windows.lock().unwrap(); + if let Some(previous) = windows.get(previous) { + if let Some(sessions) = previous.user_data().get::() { + for session in &*sessions.0.borrow() { + session.cursor_leave(seat, InputType::Pointer) + } } + PointerTarget::leave(previous, seat, data, serial, time); } - PointerTarget::leave( - &p.windows.lock().unwrap()[previous], - seat, - data, - serial, - time, - ); - if let Some(sessions) = p.windows.lock().unwrap()[active] - .user_data() - .get::() - { + + if let Some(sessions) = windows[active].user_data().get::() { for session in &*sessions.0.borrow() { session.cursor_enter(seat, InputType::Pointer) } } PointerTarget::enter( - &p.windows.lock().unwrap()[active], + &windows[active], seat, data, &MotionEvent { @@ -310,8 +374,240 @@ impl CosmicStack { impl Program for CosmicStackInternal { type Message = (); - fn view(&self) -> Element<'_, Self::Message> { - cosmic::iced::widget::text("TODO").into() + fn view(&self) -> CosmicElement<'_, Self::Message> { + let windows = self.windows.lock().unwrap(); + let Some(width) = self + .geometry + .lock() + .unwrap() + .as_ref() + .map(|r| r.size.w) + else { + return iced_widget::row(Vec::new()).into(); + }; + let tab_region = width - 128 - 8; // 64 left, 64 right + last rule with padding + let active = self.active.load(Ordering::SeqCst); + let activated = self.activated.load(Ordering::SeqCst); + let group_focused = self.group_focused.load(Ordering::SeqCst); + + let mut elements = vec![cosmic_widget::icon("view-paged-symbolic", 16) + .force_svg(true) + .style(if group_focused { + theme::Svg::custom(|theme| iced_widget::svg::Appearance { + color: Some(if theme.cosmic().is_dark { + Color::BLACK + } else { + Color::WHITE + }), + }) + } else { + theme::Svg::Symbolic + }) + .apply(iced_widget::container) + .padding([4, 24]) + .center_y() + .into()]; + + const ACTIVE_TAB_WIDTH: i32 = 140; + const MIN_TAB_WIDTH: i32 = 36; + let tab_width = if windows.len() == 1 { + tab_region + } else { + let potential_width = tab_region / windows.len() as i32; + if potential_width < ACTIVE_TAB_WIDTH { + (tab_region - ACTIVE_TAB_WIDTH) / (windows.len() - 1) as i32 + } else { + potential_width + } + }; + let scrolling = tab_width < MIN_TAB_WIDTH; + + let mut tabs = Vec::new(); + for (i, window) in windows.iter().enumerate() { + let mut tab_elements = Vec::new(); + + let app_id = window.app_id(); + let title = window.title(); + let is_active = i == active; + let was_previous_active = i.checked_sub(1).map(|i| i == active).unwrap_or(false); + let tab_width = tab_width.max(if is_active { + ACTIVE_TAB_WIDTH + } else { + MIN_TAB_WIDTH + }); + + tabs.push( + iced_widget::vertical_rule(4) + .style( + if is_active || was_previous_active || (i == 0 && group_focused) { + if activated { + theme::Rule::custom(|theme| iced_widget::rule::Appearance { + color: theme.cosmic().accent_color().into(), + width: 4, + radius: 0., + fill_mode: FillMode::Full, + }) + } else { + theme::Rule::custom(|theme| iced_widget::rule::Appearance { + color: theme.cosmic().palette.neutral_5.into(), + width: 4, + radius: 0., + fill_mode: FillMode::Full, + }) + } + } else { + theme::Rule::custom(|theme| iced_widget::rule::Appearance { + color: theme.cosmic().palette.neutral_5.into(), + width: 4, + radius: 8., + fill_mode: FillMode::Padded(4), + }) + }, + ) + .into(), + ); + + tab_elements.push( + cosmic_widget::icon(app_id, 12) + .apply(iced_widget::container) + .height(Length::Fill) + .center_y() + .into(), + ); + + let text_width = tab_width - if tab_width > 125 { 64 } else { 40 }; + if text_width > 0 { + tab_elements.push( + cosmic_widget::text(title) + .size(14) + .font(if is_active && self.activated.load(Ordering::SeqCst) { + cosmic::font::FONT_SEMIBOLD + } else { + cosmic::font::FONT + }) + .horizontal_alignment(alignment::Horizontal::Left) + .vertical_alignment(alignment::Vertical::Center) + .height(Length::Fill) + .width(text_width as u16) + .into(), + ); + } + + if tab_width > 125 { + tab_elements.push( + cosmic_widget::icon("window-close-symbolic", 16) + .force_svg(true) + .style(theme::Svg::Symbolic) + .apply(iced_widget::container) + .height(Length::Fill) + .center_y() + .into(), + ); + } + + tabs.push( + iced_widget::row(tab_elements) + .height(Length::Fill) + .width(tab_width as u16 - 22) + .spacing(8) + .apply(iced_widget::container) + .padding([2, 10]) + .center_y() + .style(if is_active { + if activated { + theme::Container::custom(|theme| iced_widget::container::Appearance { + text_color: Some(Color::from(theme.cosmic().accent_text_color())), + background: Some(cosmic::iced::Background::Color( + Color::from_rgba(1.0, 1.0, 1.0, 0.1), + )), + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }) + } else { + theme::Container::custom(|_theme| iced_widget::container::Appearance { + text_color: None, + background: Some(cosmic::iced::Background::Color( + Color::from_rgba(1.0, 1.0, 1.0, 0.1), + )), + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }) + } + } else { + theme::Container::Transparent + }) + .into(), + ) + } + + let last_was_active = active == windows.len() - 1; + let group_focused_clone = self.group_focused.clone(); + tabs.push( + iced_widget::vertical_rule(4) + .style( + if last_was_active || group_focused_clone.load(Ordering::SeqCst) { + if activated { + theme::Rule::custom(|theme| iced_widget::rule::Appearance { + color: theme.cosmic().accent_color().into(), + width: 4, + radius: 0., + fill_mode: FillMode::Full, + }) + } else { + theme::Rule::custom(|theme| iced_widget::rule::Appearance { + color: theme.cosmic().palette.neutral_5.into(), + width: 4, + radius: 0., + fill_mode: FillMode::Full, + }) + } + } else { + theme::Rule::custom(|theme| iced_widget::rule::Appearance { + color: theme.cosmic().palette.neutral_5.into(), + width: 4, + radius: 8., + fill_mode: FillMode::Padded(4), + }) + }, + ) + .into(), + ); + + let tabs = + iced_widget::row(tabs) + .apply(iced_widget::container) + .style(theme::Container::custom(|theme| { + iced_widget::container::Appearance { + text_color: None, + background: Some(cosmic::iced::Background::Color(Color::from( + theme.cosmic().palette.neutral_3, + ))), + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + } + })); + if scrolling { + elements.push( + iced_widget::Scrollable::new(tabs) + .height(Length::Fill) + .width(tab_region as u16) + .into(), + ) + } else { + elements.push(tabs.into()); + } + + elements.push(iced_widget::horizontal_space(64).into()); + + iced_widget::row(elements) + .height(TAB_HEIGHT as u16) + .width(width as u16) + .apply(iced_widget::container) + .center_y() + .into() } fn clip_mask(&self, size: Size, scale: f32) -> tiny_skia::Mask { @@ -350,11 +646,46 @@ impl Program for CosmicStackInternal { } fn background_color(&self) -> Color { - Color { - r: 0.1176, - g: 0.1176, - b: 0.1176, - a: 1.0, + if self.group_focused.load(Ordering::SeqCst) { + Color::from(cosmic::theme::COSMIC_DARK.accent_color()) + } else { + Color::from(cosmic::theme::COSMIC_DARK.palette.neutral_3) + } + } + + fn foreground( + &self, + pixels: &mut tiny_skia::PixmapMut<'_>, + damage: &[Rectangle], + scale: f32, + ) { + if self.group_focused.load(Ordering::SeqCst) { + let border = Rectangle::from_loc_and_size( + (0, TAB_HEIGHT - scale as i32), + (pixels.width() as i32, scale as i32), + ); + let mask = self.mask.lock().unwrap(); + + let mut paint = tiny_skia::Paint::default(); + let (b, g, r, a) = theme::COSMIC_DARK.accent_color().into_components(); + paint.set_color(tiny_skia::Color::from_rgba(r, g, b, a).unwrap()); + + for rect in damage { + if let Some(overlap) = rect.intersection(border) { + pixels.fill_rect( + tiny_skia::Rect::from_xywh( + overlap.loc.x as f32, + overlap.loc.y as f32, + overlap.size.w as f32, + overlap.size.h as f32, + ) + .unwrap(), + &paint, + Default::default(), + mask.as_ref(), + ) + } + } } } } @@ -390,13 +721,17 @@ impl SpaceElement for CosmicStack { } fn set_activate(&self, activated: bool) { SpaceElement::set_activate(&self.0, activated); + self.0.force_redraw(); self.0.with_program(|p| { - p.windows - .lock() - .unwrap() - .iter() - .for_each(|w| SpaceElement::set_activate(w, activated)) - }) + p.activated.store(activated, Ordering::SeqCst); + if !p.group_focused.load(Ordering::SeqCst) { + p.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::set_activate(w, activated)) + } + }); } fn output_enter(&self, output: &Output, overlap: Rectangle) { SpaceElement::output_enter(&self.0, output, overlap); @@ -432,6 +767,7 @@ impl SpaceElement for CosmicStack { }) } fn refresh(&self) { + SpaceElement::refresh(&self.0); self.0.with_program(|p| { let mut windows = p.windows.lock().unwrap(); @@ -448,8 +784,8 @@ impl SpaceElement for CosmicStack { .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| { (active >= len).then_some(len - 1) }); - windows.iter().for_each(|w| SpaceElement::refresh(w)) - }) + windows.iter().for_each(|w| SpaceElement::refresh(w)); + }); } } @@ -475,7 +811,9 @@ impl KeyboardTarget for CosmicStack { } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { let active = self.keyboard_leave_if_previous(seat, data, serial); + self.0.force_redraw(); self.0.with_program(|p| { + p.group_focused.store(false, Ordering::SeqCst); KeyboardTarget::leave(&p.windows.lock().unwrap()[active], seat, data, serial) }) } @@ -490,15 +828,17 @@ impl KeyboardTarget for CosmicStack { ) { 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, - ) + if !p.group_focused.load(Ordering::SeqCst) { + KeyboardTarget::key( + &p.windows.lock().unwrap()[active], + seat, + data, + key, + state, + serial, + time, + ) + } }) } fn modifiers( @@ -510,13 +850,15 @@ impl KeyboardTarget for CosmicStack { ) { 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, - ) + if !p.group_focused.load(Ordering::SeqCst) { + KeyboardTarget::modifiers( + &p.windows.lock().unwrap()[active], + seat, + data, + modifiers, + serial, + ) + } }) } } @@ -630,14 +972,27 @@ impl PointerTarget for CosmicStack { 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, - ) - }), + Focus::Window => { + if self.0.with_program(|p| { + PointerTarget::button( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + seat, + data, + event, + ); + if p.group_focused.swap(false, Ordering::SeqCst) { + p.windows.lock().unwrap().iter().for_each(|w| { + SpaceElement::set_activate(w, true); + w.send_configure(); + }); + true + } else { + false + } + }) { + self.0.force_redraw(); + } + } _ => {} } } @@ -702,39 +1057,206 @@ impl PointerTarget for CosmicStack { } } -render_elements! { - pub CosmicStackRenderElement where R: ImportAll + ImportMem; - Header=MemoryRenderBufferRenderElement, - Window=WaylandSurfaceRenderElement, +pub enum CosmicStackRenderElement +where + R: ImportAll + ImportMem + AsGlowRenderer + Renderer, + ::TextureId: 'static, +{ + Header(MemoryRenderBufferRenderElement), + Window(WaylandSurfaceRenderElement), +} + +impl From> for CosmicStackRenderElement +where + R: ImportAll + ImportMem + AsGlowRenderer + Renderer, + ::TextureId: 'static, +{ + fn from(elem: WaylandSurfaceRenderElement) -> Self { + CosmicStackRenderElement::Window(elem) + } +} + +impl From> for CosmicStackRenderElement +where + R: ImportAll + ImportMem + AsGlowRenderer + Renderer, + ::TextureId: 'static, +{ + fn from(elem: MemoryRenderBufferRenderElement) -> Self { + CosmicStackRenderElement::Header(elem) + } +} + +impl Element for CosmicStackRenderElement +where + R: AsGlowRenderer + Renderer + ImportAll + ImportMem, + ::TextureId: 'static, +{ + fn id(&self) -> &Id { + match self { + CosmicStackRenderElement::Header(h) => h.id(), + CosmicStackRenderElement::Window(w) => w.id(), + } + } + + fn current_commit(&self) -> CommitCounter { + match self { + CosmicStackRenderElement::Header(h) => h.current_commit(), + CosmicStackRenderElement::Window(w) => w.current_commit(), + } + } + + fn src(&self) -> Rectangle { + match self { + CosmicStackRenderElement::Header(h) => h.src(), + CosmicStackRenderElement::Window(w) => w.src(), + } + } + + fn geometry(&self, scale: Scale) -> Rectangle { + match self { + CosmicStackRenderElement::Header(h) => h.geometry(scale), + CosmicStackRenderElement::Window(w) => w.geometry(scale), + } + } + + fn location(&self, scale: Scale) -> Point { + match self { + CosmicStackRenderElement::Header(h) => h.location(scale), + CosmicStackRenderElement::Window(w) => w.location(scale), + } + } + + fn transform(&self) -> Transform { + match self { + CosmicStackRenderElement::Header(h) => h.transform(), + CosmicStackRenderElement::Window(w) => w.transform(), + } + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> Vec> { + match self { + CosmicStackRenderElement::Header(h) => h.damage_since(scale, commit), + CosmicStackRenderElement::Window(w) => w.damage_since(scale, commit), + } + } + + fn opaque_regions(&self, scale: Scale) -> Vec> { + match self { + CosmicStackRenderElement::Header(h) => h.opaque_regions(scale), + CosmicStackRenderElement::Window(w) => w.opaque_regions(scale), + } + } + + fn alpha(&self) -> f32 { + match self { + CosmicStackRenderElement::Header(h) => h.alpha(), + CosmicStackRenderElement::Window(w) => w.alpha(), + } + } +} + +impl RenderElement for CosmicStackRenderElement { + fn draw<'a>( + &self, + frame: &mut ::Frame<'a>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), ::Error> { + match self { + CosmicStackRenderElement::Header(h) => h.draw(frame, src, dst, damage), + CosmicStackRenderElement::Window(w) => w.draw(frame, src, dst, damage), + } + } + + fn underlying_storage( + &self, + renderer: &mut GlowRenderer, + ) -> Option { + match self { + CosmicStackRenderElement::Header(h) => h.underlying_storage(renderer), + CosmicStackRenderElement::Window(w) => w.underlying_storage(renderer), + } + } +} + +impl<'a, 'b> RenderElement> + for CosmicStackRenderElement> +{ + fn draw<'c>( + &self, + frame: &mut GlMultiFrame<'a, 'b, 'c>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), GlMultiError> { + match self { + CosmicStackRenderElement::Header(h) => h + .draw(frame.glow_frame_mut(), src, dst, damage) + .map_err(|err| GlMultiError::Render(err)), + CosmicStackRenderElement::Window(w) => w.draw(frame, src, dst, damage), + } + } + + fn underlying_storage( + &self, + renderer: &mut GlMultiRenderer<'a, 'b>, + ) -> Option { + match self { + CosmicStackRenderElement::Header(h) => { + h.underlying_storage(renderer.glow_renderer_mut()) + } + CosmicStackRenderElement::Window(w) => w.underlying_storage(renderer), + } + } } impl AsRenderElements for CosmicStack where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, + CosmicStackRenderElement: RenderElement, { type RenderElement = CosmicStackRenderElement; fn render_elements>( &self, renderer: &mut R, - mut location: Point, + location: Point, scale: Scale, alpha: f32, ) -> Vec { - let mut elements = AsRenderElements::::render_elements::>( - &self.0, renderer, location, scale, alpha, - ); - location.y += TAB_HEIGHT; + let stack_loc = location + + self + .0 + .with_program(|p| { + p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] + .geometry() + .loc + }) + .to_physical_precise_round(scale); + let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); - elements.extend(self.0.with_program(|p| { - let elements = AsRenderElements::::render_elements::>( - &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], - renderer, - location, + let mut elements = + AsRenderElements::::render_elements::>( + &self.0, + renderer.glow_renderer_mut(), + stack_loc, scale, alpha, ); - elements + + elements.extend(self.0.with_program(|p| { + AsRenderElements::::render_elements::>( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + renderer, + window_loc, + scale, + alpha, + ) })); elements.into_iter().map(C::from).collect() diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 6b034837..fedaec1c 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -189,6 +189,19 @@ impl CosmicSurface { } } + pub fn try_force_undecorated(&self, enable: bool) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|pending| { + pending.decoration_mode = if enable { + Some(DecorationMode::ServerSide) + } else { + None + }; + }), + _ => {} + } + } + pub fn is_resizing(&self, pending: bool) -> Option { match self { CosmicSurface::Wayland(window) => { diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 36ce7fe8..81518b28 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -284,6 +284,7 @@ impl Program for CosmicWindowInternal { &self, pixels: &mut tiny_skia::PixmapMut<'_>, damage: &[Rectangle], + _scale: f32, ) { if !self.window.is_activated(false) { let mask = self.mask.lock().unwrap(); @@ -755,12 +756,17 @@ where ) }); if has_ssd { + let ssd_loc = location + + self + .0 + .with_program(|p| p.window.geometry().loc) + .to_physical_precise_round(scale); elements.extend(AsRenderElements::::render_elements::< CosmicWindowRenderElement, >( &self.0, renderer.glow_renderer_mut(), - location, + ssd_loc, scale, alpha, )) diff --git a/src/shell/layout/floating/grabs/moving.rs b/src/shell/layout/floating/grabs/moving.rs index 42a83a81..d5f40656 100644 --- a/src/shell/layout/floating/grabs/moving.rs +++ b/src/shell/layout/floating/grabs/moving.rs @@ -3,7 +3,10 @@ use crate::{ backend::render::{element::AsGlowRenderer, IndicatorShader}, shell::{ - element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement}, + element::{ + stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped, + CosmicMappedRenderElement, + }, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, CosmicSurface, }, @@ -45,6 +48,7 @@ impl MoveGrabState { ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, I: From>, { #[cfg(feature = "debug")] diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index e00d6fe7..a8d05921 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -15,7 +15,10 @@ use std::collections::HashMap; use crate::{ backend::render::{element::AsGlowRenderer, IndicatorShader}, shell::{ - element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement}, + element::{ + stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped, + CosmicMappedRenderElement, + }, grabs::ResizeEdge, CosmicSurface, }, @@ -358,6 +361,7 @@ impl FloatingLayout { ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, { #[cfg(feature = "debug")] puffin::profile_function!(); diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 8e6279a7..ac741955 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -4,8 +4,8 @@ use crate::{ backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, GROUP_COLOR}, shell::{ element::{ - window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement, - CosmicStack, CosmicWindow, + stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped, + CosmicMappedRenderElement, CosmicStack, CosmicWindow, }, focus::{ target::{KeyboardFocusTarget, WindowGroup}, @@ -1668,6 +1668,7 @@ impl TilingLayout { ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, { #[cfg(feature = "debug")] puffin::profile_function!(); @@ -2044,6 +2045,7 @@ where ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, { if let Some(root) = reference_tree.root_node_id() { let geometries = geometries.unwrap_or_default(); @@ -2170,6 +2172,7 @@ where ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, { let focused = seat .and_then(|seat| TilingLayout::currently_focused_node(&target_tree, seat)) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index e00504f0..d23edefa 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -47,7 +47,7 @@ use std::{collections::HashMap, time::Instant}; use tracing::warn; use super::{ - element::{window::CosmicWindowRenderElement, CosmicMapped}, + element::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped}, focus::{FocusStack, FocusStackMut}, grabs::{ResizeEdge, ResizeGrab}, CosmicMappedRenderElement, CosmicSurface, @@ -483,6 +483,7 @@ impl Workspace { ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { #[cfg(feature = "debug")] diff --git a/src/utils/iced.rs b/src/utils/iced.rs index 9698c759..4f41825f 100644 --- a/src/utils/iced.rs +++ b/src/utils/iced.rs @@ -101,8 +101,9 @@ pub trait Program { &self, pixels: &mut tiny_skia::PixmapMut<'_>, damage: &[Rectangle], + scale: f32, ) { - let _ = (pixels, damage); + let _ = (pixels, damage, scale); } } @@ -563,6 +564,7 @@ impl SpaceElement for IcedElement

{ ), ); } + internal.update(true); } } @@ -650,7 +652,10 @@ where }) .collect::>(); - state_ref.program().0.foreground(&mut pixels, &damage); + state_ref + .program() + .0 + .foreground(&mut pixels, &damage, scale.x as f32); Result::<_, ()>::Ok(damage) }) diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index cc920b65..08c62492 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -49,8 +49,8 @@ use crate::{ render_output, render_workspace, CursorMode, CLEAR_COLOR, }, shell::{ - element::window::CosmicWindowRenderElement, CosmicMappedRenderElement, CosmicSurface, - WorkspaceRenderElement, + element::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement}, + CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement, }, state::{BackendData, ClientState, Common, Data, State}, utils::prelude::OutputExt, @@ -672,6 +672,7 @@ pub fn render_output_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { let cursor_mode = match session.cursor_mode() { @@ -805,6 +806,7 @@ pub fn render_workspace_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, + CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { let cursor_mode = match session.cursor_mode() {