Improve app_icon handling

timeout_old
Tassilo Horn 3 years ago
parent 6ec79af970
commit 974073f648
  1. 6
      src/con.rs
  2. 79
      src/util.rs

@ -216,7 +216,10 @@ impl<'a> DisplayFormat for Window<'a> {
let html_escape = cfg.get_format_html_escape(); let html_escape = cfg.get_format_html_escape();
let icon_dirs = cfg.get_format_icon_dirs(); let icon_dirs = cfg.get_format_icon_dirs();
// fallback_icon has no default value. // fallback_icon has no default value.
let fallback_icon = cfg.get_format_fallback_icon(); let fallback_icon: Option<Box<std::path::Path>> = cfg
.get_format_fallback_icon()
.as_ref()
.map(|i| std::path::Path::new(i).to_owned().into_boxed_path());
// Some apps report, e.g., Gimp-2.10 but the icon is still named // Some apps report, e.g., Gimp-2.10 but the icon is still named
// gimp.png. // gimp.png.
@ -269,6 +272,7 @@ impl<'a> DisplayFormat for Window<'a> {
) )
}) })
.or(fallback_icon) .or(fallback_icon)
.map(|i| i.to_string_lossy().into_owned())
.unwrap_or_else(String::new) .unwrap_or_else(String::new)
.as_str(), .as_str(),
) )

@ -19,6 +19,7 @@ use crate::config as cfg;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use std::path as p;
use std::process as proc; use std::process as proc;
pub fn get_swayr_socket_path() -> String { pub fn get_swayr_socket_path() -> String {
@ -43,25 +44,40 @@ pub fn get_swayr_socket_path() -> String {
) )
} }
fn desktop_entries() -> Vec<String> { fn desktop_entry_folders() -> Vec<Box<p::Path>> {
let mut dirs = vec![]; let mut dirs: Vec<Box<p::Path>> = vec![];
if let Some(dd) = directories::BaseDirs::new() if let Some(dd) = directories::BaseDirs::new() {
.map(|b| b.data_local_dir().to_string_lossy().to_string()) dirs.push(dd.data_local_dir().to_path_buf().into_boxed_path());
{ }
dirs.push(dd); dirs.push(
p::Path::new("/usr/share/applications/")
.to_path_buf()
.into_boxed_path(),
);
if let Ok(xdg_data_dirs) = std::env::var("XDG_DATA_DIRS") {
for mut dir in std::env::split_paths(&xdg_data_dirs) {
dir.push("applications/");
dirs.push(dir.into_boxed_path());
}
} }
dirs.push(String::from("/usr/share/applications/"));
dirs.sort();
dirs.dedup();
dirs
}
fn desktop_entries() -> Vec<Box<p::Path>> {
let mut entries = vec![]; let mut entries = vec![];
for dir in dirs { for dir in desktop_entry_folders() {
if let Ok(readdir) = std::fs::read_dir(dir) { if let Ok(readdir) = dir.read_dir() {
for entry in readdir.flatten() { for entry in readdir.flatten() {
let path = entry.path(); let path = entry.path();
if path.is_file() if path.is_file()
&& path.extension().map(|ext| ext == "desktop") && path.extension().map(|ext| ext == "desktop")
== Some(true) == Some(true)
{ {
entries.push(path.as_path().to_string_lossy().to_string()); entries.push(path.to_path_buf().into_boxed_path());
} }
} }
} }
@ -69,22 +85,30 @@ fn desktop_entries() -> Vec<String> {
entries entries
} }
fn find_icon(icon_name: &str, icon_dirs: &[String]) -> Option<String> { fn find_icon(icon_name: &str, icon_dirs: &[String]) -> Option<Box<p::Path>> {
if std::path::Path::new(icon_name).is_file() { let p = p::Path::new(icon_name);
return Some(String::from(icon_name)); if p.is_file() {
println!("(1) Icon name '{}' -> {}", icon_name, p.display());
return Some(p.to_path_buf().into_boxed_path());
} }
for dir in icon_dirs { for dir in icon_dirs {
for ext in &["png", "svg"] { for ext in &["png", "svg"] {
let mut pb = std::path::PathBuf::from(dir); let mut pb = p::PathBuf::from(dir);
pb.push(icon_name.to_owned() + "." + ext); pb.push(icon_name.to_owned() + "." + ext);
let icon_file = pb.as_path(); let icon_file = pb.as_path();
if icon_file.is_file() { if icon_file.is_file() {
return Some(String::from(icon_file.to_str().unwrap())); println!(
"(2) Icon name '{}' -> {}",
icon_name,
icon_file.display()
);
return Some(icon_file.to_path_buf().into_boxed_path());
} }
} }
} }
println!("(3) No icon for name {}", icon_name);
None None
} }
@ -95,14 +119,16 @@ lazy_static! {
regex::Regex::new(r"^(?:[a-zA-Z0-9-]+\.)+([a-zA-Z0-9-]+)$").unwrap(); regex::Regex::new(r"^(?:[a-zA-Z0-9-]+\.)+([a-zA-Z0-9-]+)$").unwrap();
} }
fn get_app_id_to_icon_map(icon_dirs: &[String]) -> HashMap<String, String> { fn get_app_id_to_icon_map(
let mut map = HashMap::new(); icon_dirs: &[String],
) -> HashMap<String, Box<p::Path>> {
let mut map: HashMap<String, Box<p::Path>> = HashMap::new();
for e in desktop_entries() { for e in desktop_entries() {
if let Ok(f) = std::fs::File::open(&e) { if let Ok(f) = std::fs::File::open(&e) {
let buf = std::io::BufReader::new(f); let buf = std::io::BufReader::new(f);
let mut wm_class: Option<String> = None; let mut wm_class: Option<String> = None;
let mut icon: Option<String> = None; let mut icon: Option<Box<p::Path>> = None;
// Get App-Id and Icon from desktop file. // Get App-Id and Icon from desktop file.
for line in buf.lines() { for line in buf.lines() {
@ -135,11 +161,7 @@ fn get_app_id_to_icon_map(icon_dirs: &[String]) -> HashMap<String, String> {
// Some apps have a reverse domain name desktop file, e.g., // Some apps have a reverse domain name desktop file, e.g.,
// org.gnome.eog.desktop but reports as just eog. // org.gnome.eog.desktop but reports as just eog.
let desktop_file_name = String::from( let desktop_file_name = String::from(
std::path::Path::new(&e) e.with_extension("").file_name().unwrap().to_string_lossy(),
.with_extension("")
.file_name()
.unwrap()
.to_string_lossy(),
); );
if let Some(caps) = if let Some(caps) =
REV_DOMAIN_NAME_RX.captures(&desktop_file_name) REV_DOMAIN_NAME_RX.captures(&desktop_file_name)
@ -157,22 +179,27 @@ fn get_app_id_to_icon_map(icon_dirs: &[String]) -> HashMap<String, String> {
} }
} }
println!(
"Desktop entries to icon files ({} entries):\n{:#?}",
map.len(),
map
);
map map
} }
lazy_static! { lazy_static! {
static ref APP_ID_TO_ICON_MAP: std::sync::Mutex<Option<HashMap<String, String>>> = static ref APP_ID_TO_ICON_MAP: std::sync::Mutex<Option<HashMap<String, Box<p::Path>>>> =
std::sync::Mutex::new(None); std::sync::Mutex::new(None);
} }
pub fn get_icon(app_id: &str, icon_dirs: &[String]) -> Option<String> { pub fn get_icon(app_id: &str, icon_dirs: &[String]) -> Option<Box<p::Path>> {
let mut opt = APP_ID_TO_ICON_MAP.lock().unwrap(); let mut opt = APP_ID_TO_ICON_MAP.lock().unwrap();
if opt.is_none() { if opt.is_none() {
opt.replace(get_app_id_to_icon_map(icon_dirs)); opt.replace(get_app_id_to_icon_map(icon_dirs));
} }
opt.as_ref().unwrap().get(app_id).map(String::from) opt.as_ref().unwrap().get(app_id).map(|i| i.to_owned())
} }
#[test] #[test]

Loading…
Cancel
Save