perf: refactor to reduce memory allocations and cpu work

This commit is contained in:
Cheong Lau 2025-10-05 16:36:59 +10:00 committed by Michael Murphy
parent 0c3e3c8629
commit dd0158d8f0
24 changed files with 234 additions and 242 deletions

View file

@ -53,7 +53,6 @@ use cosmic_app_list_config::{APP_ID, AppListConfig};
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State;
use futures::future::pending;
use iced::{Alignment, Background, Length};
use itertools::Itertools;
use std::{borrow::Cow, collections::HashMap, path::PathBuf, rc::Rc, str::FromStr, time::Duration};
use switcheroo_control::Gpu;
use tokio::time::sleep;
@ -177,31 +176,24 @@ impl DockItem {
.width(app_icon.icon_size.into())
.height(app_icon.icon_size.into());
let dots = if toplevels.is_empty() {
(0..1)
.map(|_| {
container(vertical_space().height(Length::Fixed(0.0)))
.padding(app_icon.dot_radius)
.into()
})
.collect_vec()
} else {
(0..1)
.map(|_| {
container(if toplevels.len() == 1 {
vertical_space().height(Length::Fixed(0.0))
} else {
match applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => {
vertical_space().height(app_icon.bar_size)
}
PanelAnchor::Top | PanelAnchor::Bottom => {
horizontal_space().width(app_icon.bar_size)
}
}
})
.padding(app_icon.dot_radius)
.class(theme::style::Container::Custom(Box::new(move |theme| {
let dot_constructor = || {
let space = if toplevels.len() <= 1 {
vertical_space().height(Length::Fixed(0.0))
} else {
match applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => {
vertical_space().height(app_icon.bar_size)
}
PanelAnchor::Top | PanelAnchor::Bottom => {
horizontal_space().width(app_icon.bar_size)
}
}
};
let mut container = container(space).padding(app_icon.dot_radius);
if !toplevels.is_empty() {
container =
container.class(theme::style::Container::Custom(Box::new(move |theme| {
container::Style {
text_color: Some(Color::TRANSPARENT),
background: if is_focused {
@ -218,34 +210,35 @@ impl DockItem {
icon_color: Some(Color::TRANSPARENT),
}
})))
.into()
})
.collect_vec()
}
container.into()
};
let dots = std::iter::repeat_with(dot_constructor).take(2);
let icon_wrapper: Element<_> = match applet.anchor {
PanelAnchor::Left => row(vec![
PanelAnchor::Left => row([
column(dots).into(),
horizontal_space().width(Length::Fixed(1.0)).into(),
cosmic_icon.clone().into(),
])
.align_y(Alignment::Center)
.into(),
PanelAnchor::Right => row(vec![
PanelAnchor::Right => row([
cosmic_icon.clone().into(),
horizontal_space().width(Length::Fixed(1.0)).into(),
column(dots).into(),
])
.align_y(Alignment::Center)
.into(),
PanelAnchor::Top => column(vec![
PanelAnchor::Top => column([
row(dots).into(),
vertical_space().height(Length::Fixed(1.0)).into(),
cosmic_icon.clone().into(),
])
.align_x(Alignment::Center)
.into(),
PanelAnchor::Bottom => column(vec![
PanelAnchor::Bottom => column([
cosmic_icon.clone().into(),
vertical_space().height(Length::Fixed(1.0)).into(),
row(dots).into(),
@ -470,7 +463,7 @@ where
img.img.clone(),
)))
} else {
Image::new(Handle::from_rgba(1, 1, vec![0, 0, 0, 255])).into()
Image::new(Handle::from_rgba(1, 1, [0u8, 0u8, 0u8, 255u8].as_slice())).into()
})
.class(Container::Custom(Box::new(move |theme| {
container::Style {
@ -590,7 +583,7 @@ fn find_desktop_entries<'a>(
app_ids.iter().map(|fav| {
let unicase_fav = fde::unicase::Ascii::new(fav.as_str());
fde::find_app_by_id(desktop_entries, unicase_fav).map_or_else(
|| fde::DesktopEntry::from_appid(fav.clone()).clone(),
|| fde::DesktopEntry::from_appid(fav.clone()),
ToOwned::to_owned,
)
})
@ -612,7 +605,7 @@ impl CosmicAppList {
.map(|(pinned_ctr, (e, original_id))| DockItem {
id: pinned_ctr as u32,
toplevels: Vec::new(),
desktop_info: e.clone(),
desktop_info: e,
original_app_id: original_id.clone(),
})
.collect();
@ -673,7 +666,7 @@ impl cosmic::Application for CosmicAppList {
} else {
self.overflow_active_popup = None;
self.overflow_favorites_popup = None;
return Task::batch(vec![destroy_popup(popup_id), destroy_popup(parent)]);
return Task::batch([destroy_popup(popup_id), destroy_popup(parent)]);
}
}
if let Some(toplevel_group) = self
@ -733,7 +726,7 @@ impl cosmic::Application for CosmicAppList {
} else {
self.overflow_active_popup = None;
self.overflow_favorites_popup = None;
return Task::batch(vec![destroy_popup(popup_id), destroy_popup(parent)]);
return Task::batch([destroy_popup(popup_id), destroy_popup(parent)]);
}
}
if let Some(toplevel_group) = self
@ -1490,7 +1483,10 @@ impl cosmic::Application for CosmicAppList {
} else {
0
};
let favorites: Vec<_> = (&mut self.pinned_list.iter().rev())
let favorites: Vec<_> = self
.pinned_list
.iter()
.rev()
.filter(|f| {
if favorite_to_remove > 0 && f.toplevels.is_empty() {
favorite_to_remove -= 1;
@ -1524,7 +1520,7 @@ impl cosmic::Application for CosmicAppList {
.desktop_info
.full_name(&self.locales)
.unwrap_or_default()
.to_string(),
.into_owned(),
self.popup.is_some(),
Message::Surface,
None,
@ -1618,7 +1614,7 @@ impl cosmic::Application for CosmicAppList {
.desktop_info
.full_name(&self.locales)
.unwrap_or_default()
.to_string(),
.into_owned(),
self.popup.is_some(),
Message::Surface,
None,
@ -1812,11 +1808,7 @@ impl cosmic::Application for CosmicAppList {
Message::Exec(exec.to_string(), None, desktop_info.terminal()),
));
} else if let Some(gpus) = self.gpus.as_ref() {
let default_idx = if desktop_info.prefers_non_default_gpu() {
gpus.iter().position(|gpu| !gpu.default).unwrap_or(0)
} else {
gpus.iter().position(|gpu| gpu.default).unwrap_or(0)
};
let default_idx = preferred_gpu_idx(desktop_info, gpus.iter());
for (i, gpu) in gpus.iter().enumerate() {
content = content.push(
menu_button(text::body(format!(
@ -2037,7 +2029,7 @@ impl cosmic::Application for CosmicAppList {
.desktop_info
.full_name(&self.locales)
.unwrap_or_default()
.to_string(),
.into_owned(),
self.popup.is_some(),
Message::Surface,
None,
@ -2101,7 +2093,10 @@ impl cosmic::Application for CosmicAppList {
0
};
let mut favorites_extra = Vec::with_capacity(favorite_to_remove);
let mut favorites: Vec<_> = (&mut self.pinned_list.iter().rev())
let mut favorites: Vec<_> = self
.pinned_list
.iter()
.rev()
.filter(|f| {
if favorite_to_remove > 0 && f.toplevels.is_empty() {
favorite_to_remove -= 1;
@ -2190,7 +2185,7 @@ impl cosmic::Application for CosmicAppList {
}
fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![
Subscription::batch([
wayland_subscription().map(Message::Wayland),
listen_with(|e, _, id| match e {
cosmic::iced_runtime::core::Event::PlatformSpecific(
@ -2299,9 +2294,9 @@ impl CosmicAppList {
if self.active_workspaces.is_empty() {
return Vec::new();
}
let current_output = self.core.applet.output_name.clone();
let current_output = self.core.applet.output_name.as_ref();
let mut focused_toplevels: Vec<ExtForeignToplevelHandleV1> = Vec::new();
let active_workspaces = self.active_workspaces.clone();
let active_workspaces = &self.active_workspaces;
for toplevel_list in self.active_list.iter().chain(self.pinned_list.iter()) {
for (t_info, _) in &toplevel_list.toplevels {
if t_info.state.contains(&State::Activated)
@ -2349,10 +2344,7 @@ impl CosmicAppList {
let is_proton_game = info.app_id == "steam_app_default";
if is_proton_game || info.app_id.ends_with(".exe") {
for entry in &self.desktop_entries {
let localised_name = entry
.name(&self.locales)
.map(|x| x.to_string())
.unwrap_or_default();
let localised_name = entry.name(&self.locales).unwrap_or_default();
if localised_name == info.title {
// if this is a proton game, we only want
@ -2378,13 +2370,7 @@ impl CosmicAppList {
fn launch_on_preferred_gpu(desktop_info: &DesktopEntry, gpus: Option<&[Gpu]>) -> Option<Message> {
let exec = desktop_info.exec()?;
let gpu_idx = gpus.map(|gpus| {
if desktop_info.prefers_non_default_gpu() {
gpus.iter().position(|gpu| !gpu.default).unwrap_or(0)
} else {
gpus.iter().position(|gpu| gpu.default).unwrap_or(0)
}
});
let gpu_idx = gpus.map(|gpus| preferred_gpu_idx(desktop_info, gpus.iter()));
Some(Message::Exec(
exec.to_string(),
@ -2393,6 +2379,14 @@ fn launch_on_preferred_gpu(desktop_info: &DesktopEntry, gpus: Option<&[Gpu]>) ->
))
}
fn preferred_gpu_idx<'a, I>(desktop_info: &DesktopEntry, mut gpus: I) -> usize
where
I: Iterator<Item = &'a Gpu>,
{
gpus.position(|gpu| gpu.default ^ desktop_info.prefers_non_default_gpu())
.unwrap_or(0)
}
#[derive(Debug, Default, Clone)]
pub struct DndPathBuf(PathBuf);
@ -2428,8 +2422,6 @@ impl AsMimeTypes for DndPathBuf {
}
fn as_bytes(&self, _mime_type: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
Some(Cow::Owned(
self.0.clone().to_str()?.to_string().into_bytes(),
))
Some(Cow::Owned(self.0.to_str()?.as_bytes().to_vec()))
}
}

View file

@ -87,10 +87,7 @@ impl OutputHandler for AppData {
if let Some(info) = self.output_state.info(&output) {
let _ = self
.tx
.unbounded_send(WaylandUpdate::Output(OutputUpdate::Add(
output.clone(),
info.clone(),
)));
.unbounded_send(WaylandUpdate::Output(OutputUpdate::Add(output, info)));
}
}
@ -103,10 +100,7 @@ impl OutputHandler for AppData {
if let Some(info) = self.output_state.info(&output) {
let _ = self
.tx
.unbounded_send(WaylandUpdate::Output(OutputUpdate::Update(
output.clone(),
info.clone(),
)));
.unbounded_send(WaylandUpdate::Output(OutputUpdate::Update(output, info)));
}
}
@ -118,7 +112,7 @@ impl OutputHandler for AppData {
) {
let _ = self
.tx
.unbounded_send(WaylandUpdate::Output(OutputUpdate::Remove(output.clone())));
.unbounded_send(WaylandUpdate::Output(OutputUpdate::Remove(output)));
}
}
@ -136,12 +130,12 @@ impl WorkspaceHandler for AppData {
.iter()
.filter_map(|handle| self.workspace_state.workspace_info(handle))
.find(|w| w.state.contains(WorkspaceUpdateState::Active))
.map(|workspace| workspace.handle.clone())
})
.map(|workspace| workspace.handle.clone())
.collect::<Vec<_>>();
let _ = self
.tx
.unbounded_send(WaylandUpdate::Workspace(active_workspaces.clone()));
.unbounded_send(WaylandUpdate::Workspace(active_workspaces));
}
}
@ -699,7 +693,6 @@ pub(crate) fn wayland_handler(
exit: false,
tx,
conn,
queue_handle: qh.clone(),
output_state: OutputState::new(&globals, &qh),
workspace_state: WorkspaceState::new(&registry_state, &qh),
toplevel_info_state: ToplevelInfoState::new(&registry_state, &qh),
@ -709,6 +702,7 @@ pub(crate) fn wayland_handler(
seat_state: SeatState::new(&globals, &qh),
shm_state: Shm::bind(&globals, &qh).unwrap(),
activation_state: ActivationState::bind::<AppData>(&globals, &qh).ok(),
queue_handle: qh,
};
loop {

View file

@ -57,11 +57,13 @@ pub struct WaylandImage {
impl WaylandImage {
pub fn new(img: image::RgbaImage) -> Self {
let width = img.width();
let height = img.height();
Self {
// TODO avoid copy?
img: Bytes::copy_from_slice(img.as_bytes()),
width: img.width(),
height: img.height(),
img: Bytes::from_owner(img.into_vec()),
width,
height,
}
}
}