From 67e06efe5778cace276022e1175c408b082ced07 Mon Sep 17 00:00:00 2001 From: Ryan Brue Date: Wed, 21 Aug 2024 16:20:44 -0500 Subject: [PATCH] Move the wayland backend into the app_tray module This PR moves the wayland backend and the app tray config into the AppTray struct in the app_tray module. This is much cleaner than having it in the overarching Panel struct. Also looking into turning the AppTray struct into an iced component, as it's relatively self-contained, and should only need a config passed into it Signed-off-by: Ryan Brue --- src/{ => app_tray}/compositor/cosmic_comp.rs | 42 +++--- src/{ => app_tray}/compositor/mod.rs | 23 +-- src/{ => app_tray}/compositor/wlr.rs | 0 src/app_tray/mod.rs | 139 +++++++++++++++++-- src/config.rs | 12 +- src/main.rs | 98 ++----------- src/settings_tray/mod.rs | 1 + 7 files changed, 174 insertions(+), 141 deletions(-) rename src/{ => app_tray}/compositor/cosmic_comp.rs (95%) rename src/{ => app_tray}/compositor/mod.rs (78%) rename src/{ => app_tray}/compositor/wlr.rs (100%) diff --git a/src/compositor/cosmic_comp.rs b/src/app_tray/compositor/cosmic_comp.rs similarity index 95% rename from src/compositor/cosmic_comp.rs rename to src/app_tray/compositor/cosmic_comp.rs index a935f80..cb82300 100644 --- a/src/compositor/cosmic_comp.rs +++ b/src/app_tray/compositor/cosmic_comp.rs @@ -43,9 +43,10 @@ use iced::{ }; use once_cell::sync::Lazy; -use crate::{ - app_tray::{self, AppTray}, +use crate::app_tray::{ + self, compositor::{WindowHandle, WindowInfo}, + AppTray, AppTrayMessage, ApplicationGroup, }; use super::WaylandOutgoing; @@ -534,9 +535,9 @@ impl CosmicCompBackend { pub fn handle_incoming( &mut self, - app_tray: &mut crate::app_tray::AppTray, + active_toplevels: &mut HashMap, incoming: CosmicIncoming, - ) -> Option> { + ) -> Option> { match incoming { CosmicIncoming::Init(wayland_sender) => { self.wayland_sender.replace(wayland_sender); @@ -546,15 +547,14 @@ impl CosmicCompBackend { CosmicIncoming::Toplevel(toplevel_update) => match toplevel_update { ToplevelUpdate::Add(handle, info) => { let app_id = info.app_id.clone(); - if app_tray.active_toplevels.contains_key(&app_id) { - app_tray - .active_toplevels + if active_toplevels.contains_key(&app_id) { + active_toplevels .get_mut(&info.app_id) .unwrap() .toplevels .insert(WindowHandle::Cosmic(handle), WindowInfo::Cosmic(info)); } else { - app_tray.active_toplevels.insert( + active_toplevels.insert( app_id.clone(), crate::app_tray::ApplicationGroup { toplevels: HashMap::from([( @@ -570,15 +570,12 @@ impl CosmicCompBackend { // TODO probably want to make sure it is removed if info.app_id.is_empty() { return Some(iced::Command::none()); - } else if !app_tray.active_toplevels.contains_key(&info.app_id) { + } else if !active_toplevels.contains_key(&info.app_id) { return Some(iced::Command::none()); } - for (t_handle, t_info) in &mut app_tray - .active_toplevels - .get_mut(&info.app_id) - .unwrap() - .toplevels + for (t_handle, t_info) in + &mut active_toplevels.get_mut(&info.app_id).unwrap().toplevels { if let WindowHandle::Cosmic(c_handle) = t_handle { if &handle == c_handle { @@ -592,7 +589,7 @@ impl CosmicCompBackend { } ToplevelUpdate::Remove(handle) => { let mut target_app_id: Option = None; - for (app_id, app_info) in app_tray.active_toplevels.iter_mut() { + for (app_id, app_info) in active_toplevels.iter_mut() { if app_info .toplevels .contains_key(&WindowHandle::Cosmic(handle.clone())) @@ -605,7 +602,7 @@ impl CosmicCompBackend { } } if let Some(app_id) = target_app_id { - app_tray.active_toplevels.remove(&app_id); + active_toplevels.remove(&app_id); } None } @@ -634,9 +631,9 @@ impl CosmicCompBackend { pub fn handle_outgoing( &mut self, - app_tray: &mut crate::app_tray::AppTray, + active_toplevels: &mut HashMap, outgoing: WaylandOutgoing, - ) -> Option> { + ) -> Option> { match outgoing { WaylandOutgoing::Exec(app_id, exec) => { println!("Sending a tokenrequest {} {}", &app_id, &exec); @@ -655,7 +652,7 @@ impl CosmicCompBackend { if let Some(tx) = self.wayland_sender.as_ref() { let _ = tx.send(WaylandRequest::Toplevel( if self - .active_window(app_tray) + .active_window(active_toplevels) .is_some_and(|x| x == WindowHandle::Cosmic(toplevel.clone())) { ToplevelRequest::Minimize(toplevel) @@ -691,13 +688,16 @@ impl CosmicCompBackend { } } - pub fn active_window(&self, app_tray: &AppTray) -> Option { + pub fn active_window( + &self, + active_toplevels: &HashMap, + ) -> Option { if self.active_workspaces.is_empty() { return None; } let mut focused_toplevels: Vec = Vec::new(); let active_workspaces = self.active_workspaces.clone(); - for (_, app_group) in app_tray.active_toplevels.iter() { + for (_, app_group) in active_toplevels.iter() { for (handle, info) in &app_group.toplevels { if let (WindowHandle::Cosmic(t_handle), WindowInfo::Cosmic(t_info)) = (handle, info) { diff --git a/src/compositor/mod.rs b/src/app_tray/compositor/mod.rs similarity index 78% rename from src/compositor/mod.rs rename to src/app_tray/compositor/mod.rs index 415354b..154a7ec 100644 --- a/src/compositor/mod.rs +++ b/src/app_tray/compositor/mod.rs @@ -1,4 +1,4 @@ -use std::env; +use std::{collections::HashMap, env}; use cctk::toplevel_info::ToplevelInfo; use cosmic_comp::CosmicCompBackend; @@ -6,6 +6,8 @@ use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::Zco use crate::app_tray::AppTray; +use super::{AppTrayMessage, ApplicationGroup}; + pub mod cosmic_comp; pub mod wlr; @@ -39,12 +41,12 @@ impl CompositorBackend { pub fn handle_message( &mut self, - app_tray: &mut AppTray, + active_toplevels: &mut HashMap, incoming: WaylandIncoming, - ) -> Option> { + ) -> Option> { match (self, incoming) { (Self::Cosmic(backend), WaylandIncoming::Cosmic(evt)) => { - backend.handle_incoming(app_tray, evt) + backend.handle_incoming(active_toplevels, evt) } (Self::NotSupported, _) => todo!(), (_, WaylandIncoming::NotSupported) => todo!(), @@ -53,18 +55,21 @@ impl CompositorBackend { pub fn handle_outgoing_message( &mut self, - app_tray: &mut AppTray, + active_toplevels: &mut HashMap, outgoing: WaylandOutgoing, - ) -> Option> { + ) -> Option> { match self { - Self::Cosmic(backend) => backend.handle_outgoing(app_tray, outgoing), + Self::Cosmic(backend) => backend.handle_outgoing(active_toplevels, outgoing), _ => todo!(), } } - pub fn active_window<'a>(&self, app_tray: &AppTray<'a>) -> Option { + pub fn active_window<'a>( + &self, + active_toplevels: &HashMap, + ) -> Option { match self { - Self::Cosmic(backend) => backend.active_window(app_tray), + Self::Cosmic(backend) => backend.active_window(active_toplevels), _ => todo!(), } } diff --git a/src/compositor/wlr.rs b/src/app_tray/compositor/wlr.rs similarity index 100% rename from src/compositor/wlr.rs rename to src/app_tray/compositor/wlr.rs diff --git a/src/app_tray/mod.rs b/src/app_tray/mod.rs index 2446896..d94c5d6 100644 --- a/src/app_tray/mod.rs +++ b/src/app_tray/mod.rs @@ -1,32 +1,151 @@ use std::{collections::HashMap, path::PathBuf}; +use cctk::wayland_client::protocol::wl_seat::WlSeat; +use compositor::WaylandIncoming; use desktop_entry::DesktopEntryCache; use freedesktop_desktop_entry::DesktopEntry; use iced::{ + event::{self, listen_with}, widget::{button, column, row, Container, Rule}, - Background, Border, Color, Length, Radius, Theme, + Background, Border, Color, Element, Length, Radius, Theme, }; use crate::{ - compositor::{WaylandOutgoing, WindowHandle, WindowInfo}, + app_tray::compositor::{CompositorBackend, WaylandOutgoing, WindowHandle, WindowInfo}, Message, }; +mod compositor; pub mod desktop_entry; #[derive(Clone, Debug)] pub struct AppTray<'a> { pub de_cache: DesktopEntryCache<'a>, pub active_toplevels: HashMap, + backend: CompositorBackend, + config: AppTrayConfig, } -impl<'a> Default for AppTray<'a> { +#[derive(Clone, Debug)] +pub struct AppTrayConfig { + pub favorites: Vec, +} + +#[derive(Clone, Debug)] +pub enum AppTrayMessage { + WaylandIn(WaylandIncoming), + WaylandOut(WaylandOutgoing), + NewSeat(WlSeat), + RemovedSeat(WlSeat), +} + +#[derive(Clone, Debug)] +pub enum AppTrayRequest { + Window(WindowOperationMessage), +} + +#[derive(Clone, Debug)] +pub enum WindowOperationMessage { + Activate(WindowHandle), + Minimize(WindowHandle), + Launch(String), +} + +impl<'a> Default for AppTrayConfig { fn default() -> Self { + Self { + favorites: vec![ + "com.system76.CosmicTerm".to_string(), + "org.mozilla.firefox".to_string(), + "org.kde.discover".to_string(), + ], + } + } +} + +impl<'a> AppTray<'a> { + pub fn new(config: AppTrayConfig) -> Self { Self { de_cache: DesktopEntryCache::new(), active_toplevels: HashMap::new(), + backend: CompositorBackend::new(), + config, } } + + pub fn handle_message( + &mut self, + app_tray_message: AppTrayMessage, + ) -> iced::Command { + match app_tray_message { + AppTrayMessage::WaylandIn(evt) => self + .backend + .handle_message(&mut self.active_toplevels, evt) + .unwrap_or(iced::Command::none()), + AppTrayMessage::WaylandOut(evt) => self + .backend + .handle_outgoing_message(&mut self.active_toplevels, evt) + .unwrap_or(iced::Command::none()), + AppTrayMessage::NewSeat(_) => { + println!("New seat!"); + iced::Command::none() + } + AppTrayMessage::RemovedSeat(_) => { + println!("Removed seat!"); + iced::Command::none() + } + } + } + + pub fn view(&self) -> iced::Element { + // Get app tray apps + let app_tray_apps = self + .config + .favorites + .iter() + .map(|x| { + let app_id = x.clone(); + ( + app_id, + self.active_toplevels + .get(x) + .cloned() + .unwrap_or(ApplicationGroup::default()), + ) + }) + .chain(self.active_toplevels.iter().filter_map(|(app_id, info)| { + if self.config.favorites.contains(app_id) { + None + } else { + Some((app_id.clone(), info.clone())) + } + })) + .map(|(app_id, group)| { + let entry = &self.de_cache.0.get(&app_id); + let active_window = self.backend.active_window(&self.active_toplevels); + + get_tray_widget(&app_id, *entry, group, active_window.map(|f| f.clone())) + }) + .map(|x| Element::from(iced::widget::container(x).width(48).height(48).padding(6))); + iced::widget::row(app_tray_apps).into() + } + + pub fn subscription(&self) -> iced::Subscription { + iced::Subscription::batch(vec![ + self.backend + .wayland_subscription() + .map(AppTrayMessage::WaylandIn), + listen_with(|e, _, _| match e { + iced::Event::PlatformSpecific(event::PlatformSpecific::Wayland( + event::wayland::Event::Seat(e, seat), + )) => match e { + event::wayland::SeatEvent::Enter => Some(AppTrayMessage::NewSeat(seat)), + event::wayland::SeatEvent::Leave => Some(AppTrayMessage::RemovedSeat(seat)), + }, + _ => None, + }), + ]) + } } #[derive(Clone, Debug, Default)] @@ -34,18 +153,12 @@ pub struct ApplicationGroup { pub toplevels: HashMap, } -impl<'a> AppTray<'a> { - pub fn get_desktop_entry(&mut self, app_id: &str) -> Option> { - self.de_cache.0.get(app_id).cloned() - } -} - pub fn get_tray_widget<'a>( app_id: &str, desktop_entry: Option<&DesktopEntry<'a>>, app_info: ApplicationGroup, active_window: Option, -) -> iced::widget::Button<'a, crate::Message> { +) -> iced::widget::Button<'a, AppTrayMessage> { let icon_path = desktop_entry .and_then(|entry| entry.icon()) .and_then(|icon| freedesktop_icons::lookup(icon).with_cache().find()) @@ -79,10 +192,10 @@ pub fn get_tray_widget<'a>( .padding(4) .on_press_maybe(if app_info.toplevels.is_empty() { desktop_entry.and_then(|entry| entry.exec()).map(|exec| { - Message::WaylandOut(WaylandOutgoing::Exec(app_id.to_string(), exec.to_string())) + AppTrayMessage::WaylandOut(WaylandOutgoing::Exec(app_id.to_string(), exec.to_string())) }) } else if app_info.toplevels.len() == 1 { - Some(Message::WaylandOut(WaylandOutgoing::Toggle( + Some(AppTrayMessage::WaylandOut(WaylandOutgoing::Toggle( app_info.toplevels.keys().next().unwrap().clone(), ))) } else { @@ -104,7 +217,7 @@ pub fn get_tray_widget<'a>( fn get_horizontal_rule<'a>( app_info: &ApplicationGroup, active_window: &Option<&WindowHandle>, -) -> Container<'a, Message> { +) -> Container<'a, AppTrayMessage> { if app_info.toplevels.is_empty() { iced::widget::container(iced::widget::Space::new( Length::Fixed(6.0), diff --git a/src/config.rs b/src/config.rs index b23aa8b..7e23ed5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,8 @@ #[derive(Debug, Clone)] -pub struct PanelConfig { - pub favorites: Vec, -} +pub struct PanelConfig {} impl<'a> Default for PanelConfig { fn default() -> Self { - Self { - favorites: vec![ - "com.system76.CosmicTerm".to_string(), - "org.mozilla.firefox".to_string(), - "org.kde.discover".to_string(), - ], - } + Self {} } } diff --git a/src/main.rs b/src/main.rs index afa23c3..1ab6202 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ -use app_tray::{AppTray, ApplicationGroup}; +use app_tray::{AppTray, AppTrayConfig, AppTrayMessage, ApplicationGroup}; use cctk::wayland_client::protocol::wl_seat::WlSeat; -use compositor::{CompositorBackend, WaylandIncoming, WaylandOutgoing, WindowHandle}; use config::PanelConfig; use iced::{ application::{ @@ -12,9 +11,8 @@ use iced::{ }; mod app_tray; -mod settings_tray; -mod compositor; mod config; +mod settings_tray; fn main() -> Result<(), iced::Error> { let settings = SctkLayerSurfaceSettings { @@ -34,15 +32,13 @@ fn main() -> Result<(), iced::Error> { struct Panel<'a> { panel_config: PanelConfig, app_tray: AppTray<'a>, - backend: CompositorBackend, } impl<'a> Default for Panel<'a> { fn default() -> Self { Self { panel_config: PanelConfig::default(), - app_tray: AppTray::default(), - backend: CompositorBackend::new(), + app_tray: AppTray::new(AppTrayConfig::default()), } } } @@ -50,22 +46,7 @@ impl<'a> Default for Panel<'a> { #[derive(Clone, Debug)] pub enum Message { Panic, - WaylandIn(WaylandIncoming), - WaylandOut(WaylandOutgoing), - NewSeat(WlSeat), - RemovedSeat(WlSeat), -} - -#[derive(Clone, Debug)] -pub enum AppTrayRequest { - Window(WindowOperationMessage), -} - -#[derive(Clone, Debug)] -pub enum WindowOperationMessage { - Activate(WindowHandle), - Minimize(WindowHandle), - Launch(String), + AppTray(AppTrayMessage), } impl<'a> Application for Panel<'a> { @@ -88,22 +69,10 @@ impl<'a> Application for Panel<'a> { Message::Panic => { panic!("Panic button pressed hehe"); } - Message::WaylandIn(evt) => self - .backend - .handle_message(&mut self.app_tray, evt) - .unwrap_or(Command::none()), - Message::WaylandOut(evt) => self - .backend - .handle_outgoing_message(&mut self.app_tray, evt) - .unwrap_or(Command::none()), - Message::NewSeat(_) => { - println!("New seat!"); - Command::none() - } - Message::RemovedSeat(_) => { - println!("Removed seat!"); - Command::none() - } + Message::AppTray(app_tray_msg) => self + .app_tray + .handle_message(app_tray_msg) + .map(Message::AppTray), } } @@ -111,43 +80,7 @@ impl<'a> Application for Panel<'a> { &self, _id: iced::window::Id, ) -> iced::Element<'_, Self::Message, Self::Theme, Self::Renderer> { - // Get app tray apps - let app_tray_apps = self - .panel_config - .favorites - .iter() - .map(|x| { - let app_id = x.clone(); - ( - app_id, - self.app_tray - .active_toplevels - .get(x) - .cloned() - .unwrap_or(ApplicationGroup::default()), - ) - }) - .chain( - self.app_tray - .active_toplevels - .iter() - .filter_map(|(app_id, info)| { - if self.panel_config.favorites.contains(app_id) { - None - } else { - Some((app_id.clone(), info.clone())) - } - }), - ) - .map(|(app_id, group)| { - let entry = self.app_tray.de_cache.0.get(&app_id); - let active_window = self.backend.active_window(&self.app_tray); - - app_tray::get_tray_widget(&app_id, entry, group, active_window.map(|f| f.clone())) - }) - .map(|x| Element::from(iced::widget::container(x).width(48).height(48).padding(6))); - - let panel_items = iced::widget::row(app_tray_apps); + let panel_items = self.app_tray.view().map(Message::AppTray); iced::widget::container(column![ iced::widget::horizontal_rule(1).style(|_| iced::widget::rule::Style { color: Color::WHITE, @@ -163,18 +96,7 @@ impl<'a> Application for Panel<'a> { } fn subscription(&self) -> iced::Subscription { - Subscription::batch(vec![ - self.backend.wayland_subscription().map(Message::WaylandIn), - listen_with(|e, _, _| match e { - iced::Event::PlatformSpecific(event::PlatformSpecific::Wayland( - event::wayland::Event::Seat(e, seat), - )) => match e { - event::wayland::SeatEvent::Enter => Some(Message::NewSeat(seat)), - event::wayland::SeatEvent::Leave => Some(Message::RemovedSeat(seat)), - }, - _ => None, - }), - ]) + self.app_tray.subscription().map(Message::AppTray) } } diff --git a/src/settings_tray/mod.rs b/src/settings_tray/mod.rs index e69de29..84c48f4 100644 --- a/src/settings_tray/mod.rs +++ b/src/settings_tray/mod.rs @@ -0,0 +1 @@ +pub fn get_tray_widget() {}