From 18e847abb8472198a40a5de3a8e82c521a55e156 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 1 Mar 2024 13:20:47 -0700 Subject: [PATCH] Calculate open with apps, when xdg feature is enabled --- Cargo.lock | 130 ++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 3 +- src/lib.rs | 2 + src/tab.rs | 43 ++++++++++----- src/xdg/mime_app.rs | 58 ++++++++++++++++++++ src/xdg/mod.rs | 5 ++ 6 files changed, 208 insertions(+), 33 deletions(-) create mode 100644 src/xdg/mime_app.rs create mode 100644 src/xdg/mod.rs diff --git a/Cargo.lock b/Cargo.lock index e2bc161..a9b7a26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1127,10 +1127,11 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "atomicwrites", "cosmic-config-derive", - "dirs", + "dirs 5.0.1", "iced_futures", "known-folders", "notify", @@ -1143,6 +1144,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "quote", "syn 1.0.109", @@ -1153,7 +1155,7 @@ name = "cosmic-files" version = "0.1.0" dependencies = [ "chrono", - "dirs", + "dirs 5.0.1", "env_logger", "fastrand 2.0.1", "fork", @@ -1182,7 +1184,7 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.11.2" -source = "git+https://github.com/pop-os/cosmic-text.git#2766961af621b9235616e186046f6d14a2f5fbc0" +source = "git+https://github.com/pop-os/cosmic-text.git#22e61965aa38ab0bd64bbdddf3848ae891edceba" dependencies = [ "bitflags 2.4.2", "fontdb", @@ -1204,6 +1206,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "almost", "cosmic-config", @@ -1348,7 +1351,7 @@ version = "0.19.0" source = "git+https://github.com/gfx-rs/wgpu?rev=20fda69#20fda698341efbdc870b8027d6d49f5bf3f36109" dependencies = [ "bitflags 2.4.2", - "libloading 0.8.1", + "libloading 0.8.2", "winapi", ] @@ -1493,13 +1496,22 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys 0.3.7", +] + [[package]] name = "dirs" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] @@ -1512,6 +1524,17 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -1567,7 +1590,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.1", + "libloading 0.8.2", ] [[package]] @@ -2021,13 +2044,26 @@ dependencies = [ "num", ] +[[package]] +name = "freedesktop-desktop-entry" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45157175a725e81f3f594382430b6b78af5f8f72db9bd51b94f0785f80fc6d29" +dependencies = [ + "dirs 3.0.2", + "gettext-rs", + "memchr", + "thiserror", + "xdg", +] + [[package]] name = "freedesktop-icons" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8ef34245e0540c9a3ce7a28340b98d2c12b75da0d446da4e8224923fcaa0c16" dependencies = [ - "dirs", + "dirs 5.0.1", "once_cell", "rust-ini", "thiserror", @@ -2222,6 +2258,26 @@ dependencies = [ "wasi", ] +[[package]] +name = "gettext-rs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49ea8a8fad198aaa1f9655a2524b64b70eb06b2f3ff37da407566c93054f364" +dependencies = [ + "gettext-sys", + "locale_config", +] + +[[package]] +name = "gettext-sys" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c63ce2e00f56a206778276704bbe38564c8695249fdc8f354b4ef71c57c3839d" +dependencies = [ + "cc", + "temp-dir", +] + [[package]] name = "gif" version = "0.11.4" @@ -2462,7 +2518,7 @@ dependencies = [ "bitflags 2.4.2", "com", "libc", - "libloading 0.8.1", + "libloading 0.8.2", "thiserror", "widestring", "winapi", @@ -2594,6 +2650,7 @@ dependencies = [ [[package]] name = "iced" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "iced_accessibility", "iced_core", @@ -2608,6 +2665,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "accesskit", "accesskit_winit", @@ -2616,6 +2674,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "bitflags 1.3.2", "log", @@ -2632,6 +2691,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "futures", "iced_core", @@ -2644,6 +2704,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2667,6 +2728,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2678,6 +2740,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "iced_core", "iced_futures", @@ -2687,6 +2750,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "iced_core", "once_cell", @@ -2696,6 +2760,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "bytemuck", "cosmic-text", @@ -2712,6 +2777,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2730,6 +2796,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "iced_renderer", "iced_runtime", @@ -2743,6 +2810,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.12.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "iced_graphics", "iced_runtime", @@ -2995,7 +3063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.1", + "libloading 0.8.2", "pkg-config", ] @@ -3086,6 +3154,7 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libcosmic" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#268b47d0002a5daa4cc50b124223737afe9a1dc5" dependencies = [ "apply", "ashpd", @@ -3094,6 +3163,7 @@ dependencies = [ "css-color", "derive_setters", "fraction", + "freedesktop-desktop-entry", "freedesktop-icons", "iced", "iced_core", @@ -3106,8 +3176,11 @@ dependencies = [ "iced_widget", "iced_winit", "lazy_static", + "mime", + "nix 0.27.1", "palette", "rfd", + "shlex", "slotmap", "taffy", "thiserror", @@ -3140,12 +3213,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" dependencies = [ "cfg-if 1.0.0", - "windows-sys 0.48.0", + "windows-targets 0.52.4", ] [[package]] @@ -3578,6 +3651,17 @@ dependencies = [ "memoffset 0.7.1", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.2", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "nom" version = "5.1.3" @@ -4797,6 +4881,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -5113,6 +5203,12 @@ version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +[[package]] +name = "temp-dir" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd16aa9ffe15fe021c6ee3766772132c6e98dfa395a167e16864f61a9cfb71d6" + [[package]] name = "tempfile" version = "3.10.1" @@ -5273,7 +5369,7 @@ checksum = "d4098d49269baa034a8d1eae9bd63e9fa532148d772121dace3bcd6a6c98eb6d" dependencies = [ "as-raw-xcb-connection", "ctor", - "libloading 0.8.1", + "libloading 0.8.2", "tracing", ] @@ -5683,9 +5779,9 @@ checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -6058,7 +6154,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.1", + "libloading 0.8.2", "log", "metal", "naga", @@ -6515,7 +6611,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.1", + "libloading 0.8.2", "once_cell", "rustix 0.38.31", "x11rb-protocol", diff --git a/Cargo.toml b/Cargo.toml index 17c5a21..f83fcc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,8 @@ features = ["serde"] git = "https://github.com/jackpot51/systemicons" [features] -default = ["wgpu"] +default = ["xdg", "wgpu"] +xdg = ["libcosmic/desktop"] wgpu = ["libcosmic/wgpu"] [profile.release-with-debug] diff --git a/src/lib.rs b/src/lib.rs index 555e6ca..9e236ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ mod mime_icon; mod mouse_area; mod operation; mod tab; +#[cfg(feature = "xdg")] +mod xdg; pub fn home_dir() -> PathBuf { match dirs::home_dir() { diff --git a/src/tab.rs b/src/tab.rs index eebf8f8..9050738 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -32,6 +32,8 @@ use std::{ time::{Duration, Instant}, }; +#[cfg(feature = "xdg")] +use crate::xdg::{mime_apps, DesktopEntryData}; use crate::{ app::Action, config::{IconSizes, TabConfig}, @@ -261,6 +263,11 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { icon_handle_grid, icon_handle_list, icon_handle_list_condensed, + #[cfg(feature = "xdg")] + open_with: mime_guess + .first() + .map(|mime| mime_apps(&mime)) + .unwrap_or_default(), thumbnail_res_opt: match mime_guess.first() { Some(mime) if mime.type_() == "image" => None, _ => Some(Err(())), @@ -295,7 +302,7 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { not(target_os = "android") ) )))] -pub fn scan_trash() -> Vec { +pub fn scan_trash(_sizes: IconSizes) -> Vec { log::warn!("viewing trash not supported on this platform"); Vec::new() } @@ -351,6 +358,8 @@ pub fn scan_trash(sizes: IconSizes) -> Vec { icon_handle_grid, icon_handle_list, icon_handle_list_condensed, + #[cfg(feature = "xdg")] + open_with: Vec::new(), thumbnail_res_opt: Some(Err(())), button_id: widget::Id::unique(), pos_opt: Cell::new(None), @@ -458,6 +467,8 @@ pub struct Item { pub icon_handle_grid: widget::icon::Handle, pub icon_handle_list: widget::icon::Handle, pub icon_handle_list_condensed: widget::icon::Handle, + #[cfg(feature = "xdg")] + pub open_with: Vec, pub thumbnail_res_opt: Option>, pub button_id: widget::Id, pub pos_opt: Cell>, @@ -544,20 +555,22 @@ impl Item { impl fmt::Debug for Item { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Item") - .field("name", &self.name) - .field("metadata", &self.metadata) - .field("hidden", &self.hidden) - .field("path", &self.path) - .field("mime_guess", &self.mime_guess) - // icon_handles - // thumbnail_res_opt - .field("button_id", &self.button_id) - .field("pos_opt", &self.pos_opt) - .field("rect_opt", &self.rect_opt) - .field("selected", &self.selected) - .field("click_time", &self.click_time) - .finish() + let mut d = f.debug_struct("Item"); + d.field("name", &self.name); + d.field("metadata", &self.metadata); + d.field("hidden", &self.hidden); + d.field("path", &self.path); + d.field("mime_guess", &self.mime_guess); + // icon_handles + #[cfg(feature = "xdg")] + d.field("open_with", &self.open_with); + // thumbnail_res_opt + d.field("button_id", &self.button_id); + d.field("pos_opt", &self.pos_opt); + d.field("rect_opt", &self.rect_opt); + d.field("selected", &self.selected); + d.field("click_time", &self.click_time); + d.finish() } } diff --git a/src/xdg/mime_app.rs b/src/xdg/mime_app.rs new file mode 100644 index 0000000..2f0e89c --- /dev/null +++ b/src/xdg/mime_app.rs @@ -0,0 +1,58 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +pub use cosmic::desktop::DesktopEntryData; +use cosmic::desktop::{load_applications, Mime}; +use once_cell::sync::Lazy; +use std::{collections::HashMap, sync::Mutex, time::Instant}; + +pub struct MimeAppCache { + cache: HashMap>, + empty: Vec, +} + +impl MimeAppCache { + pub fn new() -> Self { + let mut mime_app_cache = Self { + cache: HashMap::new(), + empty: Vec::new(), + }; + mime_app_cache.reload(); + mime_app_cache + } + + pub fn reload(&mut self) { + let start = Instant::now(); + + self.cache.clear(); + + //TODO: get proper locale? + let locale = None; + for app in load_applications(locale, false) { + for mime_type in app.mime_types.iter() { + self.cache + .entry(mime_type.clone()) + .or_insert_with(|| Vec::with_capacity(1)) + .push(app.clone()); + } + } + + for apps in self.cache.values_mut() { + apps.sort_by(|a, b| lexical_sort::natural_lexical_cmp(&a.name, &b.name)); + } + + let elapsed = start.elapsed(); + log::info!("loaded mime app cache in {:?}", elapsed); + } + + pub fn get(&self, key: &Mime) -> &Vec { + self.cache.get(&key).unwrap_or_else(|| &self.empty) + } +} + +static MIME_APP_CACHE: Lazy> = Lazy::new(|| Mutex::new(MimeAppCache::new())); + +pub fn mime_apps(mime: &Mime) -> Vec { + let mime_app_cache = MIME_APP_CACHE.lock().unwrap(); + mime_app_cache.get(mime).clone() +} diff --git a/src/xdg/mod.rs b/src/xdg/mod.rs new file mode 100644 index 0000000..bce8d5d --- /dev/null +++ b/src/xdg/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +pub mod mime_app; +pub use mime_app::*;