diff --git a/src/desktop.rs b/src/desktop.rs index c1fe8b9..9f752ef 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,8 +1,36 @@ use eyre::{Context, Result}; use freedesktop_file_parser::{DesktopFile, EntryType}; -use palette::{IntoColor, Oklab, Oklaba}; +use palette::{IntoColor, Oklab, Oklaba, color_difference::EuclideanDistance}; use std::{collections::HashMap, ffi::OsStr, fs::DirEntry, path::Path}; +pub struct DesktopEntries { + entries: Vec, +} + +pub struct DesktopEntry { + pub _id: String, + pub file: DesktopFile, + pub avg_icon_color: Oklab, +} + +impl DesktopEntries { + pub fn count(&self) -> usize { + self.entries.len() + } + pub fn find_entry(&self, color: Oklab) -> Option<&DesktopEntry> { + self.entries.iter().min_by(|x, y| { + f32::total_cmp( + &diff_color(x.avg_icon_color, color), + &diff_color(y.avg_icon_color, color), + ) + }) + } +} + +fn diff_color(icon: Oklab, color: Oklab) -> f32 { + icon.distance_squared(color) +} + fn walkdir(path: &Path, f: &mut impl FnMut(&DirEntry) -> Result<()>) -> Result<()> { for entry in path.read_dir()? { let entry = entry?; @@ -14,7 +42,7 @@ fn walkdir(path: &Path, f: &mut impl FnMut(&DirEntry) -> Result<()>) -> Result<( Ok(()) } -pub(crate) fn find_desktop_files() -> Result> { +pub(crate) fn find_desktop_files() -> Result { // https://specifications.freedesktop.org/desktop-entry/latest/file-naming.html let paths = std::env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share/:/usr/share/".into()); let paths = std::env::split_paths(&paths); @@ -55,7 +83,14 @@ pub(crate) fn find_desktop_files() -> Result> { .decode() .wrap_err_with(|| format!("decoding {}", icon.display()))?; let color = average_color(&icon); - results.insert(id, (file, color)); + results.insert( + id.clone(), + DesktopEntry { + _id: id, + file, + avg_icon_color: color, + }, + ); } Ok(()) @@ -63,7 +98,9 @@ pub(crate) fn find_desktop_files() -> Result> { .wrap_err_with(|| format!("{}", base.display()))?; } - Ok(results.into_values().collect()) + Ok(DesktopEntries { + entries: results.into_values().collect(), + }) } fn average_color(image: &image::DynamicImage) -> palette::Oklab { diff --git a/src/main.rs b/src/main.rs index cee2f7b..b3df73a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ use std::{ }; use eyre::{Context, Result, bail}; -use freedesktop_file_parser::{DesktopFile, EntryType}; +use freedesktop_file_parser::EntryType; use log::{error, info, warn}; -use palette::{FromColor, IntoColor, Oklab, color_difference::EuclideanDistance}; +use palette::{FromColor, IntoColor, Oklab}; use smithay_client_toolkit::{ compositor::{CompositorHandler, CompositorState}, output::{OutputHandler, OutputState}, @@ -33,6 +33,8 @@ use wayland_client::{ protocol::{wl_buffer, wl_output::WlOutput, wl_pointer::WlPointer, wl_seat::WlSeat, wl_shm}, }; +use crate::desktop::DesktopEntries; + fn main() -> Result<()> { env_logger::builder() .filter(None, log::LevelFilter::Info) @@ -42,7 +44,7 @@ fn main() -> Result<()> { let desktop_files = desktop::find_desktop_files().wrap_err("loading .desktop files")?; info!( "Loaded {} desktop icons in {:?}", - desktop_files.len(), + desktop_files.count(), now.elapsed() ); @@ -89,7 +91,7 @@ struct App { shm: Shm, seat_state: SeatState, - desktop_files: Vec<(DesktopFile, Oklab)>, + desktop_files: DesktopEntries, pointers: HashMap, layer_surfaces: Vec, } @@ -400,13 +402,10 @@ impl PointerHandler for App { let oklab: Oklab = srgb.into_format::().into_color(); - let best_match = self - .desktop_files - .iter() - .min_by_key(|(_, icon_color)| (oklab.distance(*icon_color) * 1000000.0) as u32); + let best_match = self.desktop_files.find_entry(oklab); if let Some(best_match) = best_match - && let EntryType::Application(app) = &best_match.0.entry.entry_type + && let EntryType::Application(app) = &best_match.file.entry.entry_type && let Some(exec) = &app.exec { // lol terrible implementation that works well enough