app_list: Use libcosmic desktop helpers

This commit is contained in:
Victoria Brekenfeld 2024-01-30 18:23:57 +00:00
parent 2a47cde1b4
commit d7a8db6a07
6 changed files with 112 additions and 248 deletions

48
Cargo.lock generated
View file

@ -792,8 +792,6 @@ dependencies = [
"anyhow", "anyhow",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"cosmic-protocols", "cosmic-protocols",
"freedesktop-desktop-entry",
"freedesktop-icons",
"futures", "futures",
"futures-util", "futures-util",
"i18n-embed 0.13.9", "i18n-embed 0.13.9",
@ -808,7 +806,6 @@ dependencies = [
"rust-embed 6.8.1", "rust-embed 6.8.1",
"rust-embed-utils 7.8.1", "rust-embed-utils 7.8.1",
"serde", "serde",
"shlex",
"tokio", "tokio",
"tracing", "tracing",
"tracing-log", "tracing-log",
@ -1036,7 +1033,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config" name = "cosmic-config"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"atomicwrites", "atomicwrites",
"cosmic-config-derive", "cosmic-config-derive",
@ -1044,17 +1041,19 @@ dependencies = [
"dirs 5.0.1", "dirs 5.0.1",
"futures-util", "futures-util",
"iced_futures", "iced_futures",
"known-folders",
"notify", "notify",
"once_cell", "once_cell",
"ron", "ron",
"serde", "serde",
"xdg",
"zbus", "zbus",
] ]
[[package]] [[package]]
name = "cosmic-config-derive" name = "cosmic-config-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
@ -1164,7 +1163,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-theme" name = "cosmic-theme"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"almost", "almost",
"cosmic-config", "cosmic-config",
@ -2689,7 +2688,7 @@ dependencies = [
[[package]] [[package]]
name = "iced" name = "iced"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"iced_accessibility", "iced_accessibility",
"iced_core", "iced_core",
@ -2704,7 +2703,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_accessibility" name = "iced_accessibility"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"accesskit_unix", "accesskit_unix",
@ -2713,7 +2712,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_core" name = "iced_core"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"iced_accessibility", "iced_accessibility",
@ -2731,7 +2730,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_futures" name = "iced_futures"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"futures", "futures",
"iced_core", "iced_core",
@ -2744,7 +2743,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_graphics" name = "iced_graphics"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"bytemuck", "bytemuck",
@ -2767,7 +2766,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_renderer" name = "iced_renderer"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"iced_graphics", "iced_graphics",
"iced_tiny_skia", "iced_tiny_skia",
@ -2780,7 +2779,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_runtime" name = "iced_runtime"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"iced_accessibility", "iced_accessibility",
"iced_core", "iced_core",
@ -2792,7 +2791,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_sctk" name = "iced_sctk"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"enum-repr", "enum-repr",
"float-cmp", "float-cmp",
@ -2816,7 +2815,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_style" name = "iced_style"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"iced_core", "iced_core",
"once_cell", "once_cell",
@ -2826,7 +2825,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_tiny_skia" name = "iced_tiny_skia"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"cosmic-text", "cosmic-text",
@ -2844,7 +2843,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_wgpu" name = "iced_wgpu"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"bytemuck", "bytemuck",
@ -2864,7 +2863,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_widget" name = "iced_widget"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"iced_renderer", "iced_renderer",
"iced_runtime", "iced_runtime",
@ -3070,6 +3069,15 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "known-folders"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4397c789f2709d23cfcb703b316e0766a8d4b17db2d47b0ab096ef6047cae1d8"
dependencies = [
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "kqueue" name = "kqueue"
version = "1.0.8" version = "1.0.8"
@ -3120,7 +3128,7 @@ checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]] [[package]]
name = "libcosmic" name = "libcosmic"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" source = "git+https://github.com/pop-os/libcosmic#bf0508816b7e7098cfcc6eb16ee288207ef0cc31"
dependencies = [ dependencies = [
"apply", "apply",
"ashpd", "ashpd",
@ -3132,6 +3140,7 @@ dependencies = [
"css-color", "css-color",
"derive_setters", "derive_setters",
"fraction", "fraction",
"freedesktop-desktop-entry",
"freedesktop-icons", "freedesktop-icons",
"iced", "iced",
"iced_core", "iced_core",
@ -3147,6 +3156,7 @@ dependencies = [
"palette", "palette",
"rfd", "rfd",
"ron", "ron",
"shlex",
"slotmap", "slotmap",
"taffy", "taffy",
"thiserror", "thiserror",

View file

@ -32,7 +32,7 @@ libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = fa
"applet-token", "applet-token",
"tokio", "tokio",
"wayland", "wayland",
"process", "desktop",
"dbus-config" "dbus-config"
] } ] }
zbus = { version = "3.14", default-features = false, features = ["tokio"] } zbus = { version = "3.14", default-features = false, features = ["tokio"] }

View file

@ -18,14 +18,11 @@ tracing-subscriber.workspace = true
tracing-log.workspace = true tracing-log.workspace = true
tracing.workspace = true tracing.workspace = true
nix = "0.26" nix = "0.26"
shlex = "1.3.0"
anyhow = "1.0" anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
log = "0.4" log = "0.4"
tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "macros", "process"] } tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "macros", "process"] }
itertools = "*" itertools = "*"
freedesktop-desktop-entry = "0.5.0"
freedesktop-icons = "0.2.4"
i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester"] } i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester"] }
i18n-embed-fl = "0.6" i18n-embed-fl = "0.6"
rust-embed = "6.3" rust-embed = "6.3"

View file

@ -12,6 +12,7 @@ use cctk::toplevel_info::ToplevelInfo;
use cctk::wayland_client::protocol::wl_data_device_manager::DndAction; use cctk::wayland_client::protocol::wl_data_device_manager::DndAction;
use cctk::wayland_client::protocol::wl_seat::WlSeat; use cctk::wayland_client::protocol::wl_seat::WlSeat;
use cosmic::cosmic_config::{self, Config, CosmicConfigEntry}; use cosmic::cosmic_config::{self, Config, CosmicConfigEntry};
use cosmic::desktop::{load_applications_for_app_ids, DesktopEntryData};
use cosmic::iced; use cosmic::iced;
use cosmic::iced::event::listen_with; use cosmic::iced::event::listen_with;
use cosmic::iced::wayland::actions::data_device::DataFromMimeType; use cosmic::iced::wayland::actions::data_device::DataFromMimeType;
@ -24,7 +25,6 @@ use cosmic::iced::widget::vertical_space;
use cosmic::iced::widget::{column, dnd_source, mouse_area, row, Column, Row}; use cosmic::iced::widget::{column, dnd_source, mouse_area, row, Column, Row};
use cosmic::iced::Color; use cosmic::iced::Color;
use cosmic::iced::{window, Subscription}; use cosmic::iced::{window, Subscription};
use cosmic::iced_core::window::Icon;
use cosmic::iced_runtime::core::alignment::Horizontal; use cosmic::iced_runtime::core::alignment::Horizontal;
use cosmic::iced_runtime::core::event; use cosmic::iced_runtime::core::event;
use cosmic::iced_sctk::commands::data_device::accept_mime_type; use cosmic::iced_sctk::commands::data_device::accept_mime_type;
@ -44,7 +44,6 @@ use cosmic::{
}; };
use cosmic::{Element, Theme}; use cosmic::{Element, Theme};
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1; use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1;
use freedesktop_desktop_entry::DesktopEntry;
use futures::future::pending; use futures::future::pending;
use iced::widget::container; use iced::widget::container;
use iced::Alignment; use iced::Alignment;
@ -52,9 +51,7 @@ use iced::Background;
use iced::Length; use iced::Length;
use itertools::Itertools; use itertools::Itertools;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
@ -71,14 +68,14 @@ pub fn run() -> cosmic::iced::Result {
struct DockItem { struct DockItem {
id: u32, id: u32,
toplevels: Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>, toplevels: Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>,
desktop_info: DesktopInfo, desktop_info: DesktopEntryData,
} }
impl DataFromMimeType for DockItem { impl DataFromMimeType for DockItem {
fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> { fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> {
if mime_type == MIME_TYPE { if mime_type == MIME_TYPE && self.desktop_info.path.is_some() {
Some( Some(
Url::from_file_path(self.desktop_info.path.clone()) Url::from_file_path(self.desktop_info.path.as_deref().unwrap())
.ok()? .ok()?
.to_string() .to_string()
.as_bytes() .as_bytes()
@ -94,7 +91,7 @@ impl DockItem {
fn new( fn new(
id: u32, id: u32,
toplevels: Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>, toplevels: Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>,
desktop_info: DesktopInfo, desktop_info: DesktopEntryData,
) -> Self { ) -> Self {
Self { Self {
id, id,
@ -167,11 +164,11 @@ impl DockItem {
dnd_source( dnd_source(
mouse_area( mouse_area(
icon_button icon_button
.on_press( .on_press_maybe(
toplevels toplevels
.first() .first()
.map(|t| Message::Activate(t.0.clone())) .map(|t| Message::Activate(t.0.clone()))
.unwrap_or_else(|| Message::Exec(desktop_info.exec.clone())), .or_else(|| desktop_info.exec.clone().map(Message::Exec)),
) )
.width(Length::Shrink) .width(Length::Shrink)
.height(Length::Shrink), .height(Length::Shrink),
@ -202,7 +199,7 @@ struct DndOffer {
#[derive(Clone, Default)] #[derive(Clone, Default)]
struct CosmicAppList { struct CosmicAppList {
core: cosmic::app::Core, core: cosmic::app::Core,
popup: Option<(window::Id, DockItem)>, popup: Option<(window::Id, u32)>,
subscription_ctr: u32, subscription_ctr: u32,
item_ctr: u32, item_ctr: u32,
active_list: Vec<DockItem>, active_list: Vec<DockItem>,
@ -246,109 +243,6 @@ enum Message {
ConfigUpdated(AppListConfig), ConfigUpdated(AppListConfig),
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum IconSource {
Name(String),
Path(PathBuf),
}
impl IconSource {
fn from_unknown(icon: &str) -> Self {
let icon_path = Path::new(icon);
if icon_path.is_absolute() && icon_path.exists() {
Self::Path(icon_path.into())
} else {
Self::Name(icon.into())
}
}
fn as_cosmic_icon(&self) -> cosmic::widget::icon::Icon {
match self {
Self::Name(name) => cosmic::widget::icon::from_name(name.as_str())
.size(128)
.fallback(Some(cosmic::widget::icon::IconFallback::Names(vec![
"application-default".into(),
"application-x-executable".into(),
])))
.into(),
Self::Path(path) => cosmic::widget::icon(cosmic::widget::icon::from_path(path.clone())),
}
}
}
impl Default for IconSource {
fn default() -> Self {
Self::Name("application-default".to_string())
}
}
#[derive(Debug, Clone, Default)]
struct DesktopInfo {
id: String,
wm_class: Option<String>,
icon: IconSource,
exec: String,
name: String,
path: PathBuf,
}
fn desktop_info_for_app_ids(mut app_ids: Vec<String>) -> Vec<DesktopInfo> {
let app_ids_clone = app_ids.clone();
let mut ret = freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths())
.filter_map(|path| {
std::fs::read_to_string(&path).ok().and_then(|input| {
DesktopEntry::decode(&path, &input).ok().and_then(|de| {
if let Some(i) = app_ids.iter().position(|s| {
s == de.appid || s.eq(&de.startup_wm_class().unwrap_or_default())
}) {
// check if absolute path exists and otherwise treat it as a name
let icon = de.icon().unwrap_or(de.appid);
let icon_path = Path::new(icon);
let icon = if icon_path.is_absolute() && icon_path.exists() {
IconSource::Path(icon_path.into())
} else {
IconSource::Name(icon.into())
};
app_ids.remove(i);
Some(DesktopInfo {
id: de.appid.to_string(),
wm_class: de.startup_wm_class().map(ToString::to_string),
icon,
exec: de.exec().unwrap_or_default().to_string(),
name: de.name(None).unwrap_or_default().to_string(),
path: path.clone(),
})
} else {
None
}
})
})
})
.collect_vec();
ret.append(
&mut app_ids
.into_iter()
.map(|id| DesktopInfo {
id,
icon: IconSource::default(),
..Default::default()
})
.collect_vec(),
);
ret.sort_by(|a, b| {
app_ids_clone
.iter()
.position(|id| id == &a.id || Some(id) == a.wm_class.as_ref())
.cmp(
&app_ids_clone
.iter()
.position(|id| id == &b.id || Some(id) == b.wm_class.as_ref()),
)
});
ret
}
fn index_in_list( fn index_in_list(
mut list_len: usize, mut list_len: usize,
item_size: f32, item_size: f32,
@ -404,15 +298,19 @@ impl cosmic::Application for CosmicAppList {
.unwrap_or_default(); .unwrap_or_default();
let mut self_ = Self { let mut self_ = Self {
core, core,
favorite_list: desktop_info_for_app_ids(config.favorites.clone()) favorite_list: load_applications_for_app_ids(
.into_iter() None,
.enumerate() config.favorites.iter().map(|s| &**s),
.map(|(favorite_ctr, e)| DockItem { true,
id: favorite_ctr as u32, )
toplevels: Default::default(), .into_iter()
desktop_info: e, .enumerate()
}) .map(|(favorite_ctr, e)| DockItem {
.collect(), id: favorite_ctr as u32,
toplevels: Default::default(),
desktop_info: e,
})
.collect(),
config, config,
..Default::default() ..Default::default()
}; };
@ -450,7 +348,7 @@ impl cosmic::Application for CosmicAppList {
}; };
let new_id = window::Id::unique(); let new_id = window::Id::unique();
self.popup = Some((new_id, toplevel_group.clone())); self.popup = Some((new_id, toplevel_group.id.clone()));
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, window::Id::MAIN,
@ -511,12 +409,12 @@ impl cosmic::Application for CosmicAppList {
} }
} }
Message::Activate(handle) => { Message::Activate(handle) => {
if let Some(p) = self.popup.take() {
return destroy_popup(p.0);
}
if let Some(tx) = self.wayland_sender.as_ref() { if let Some(tx) = self.wayland_sender.as_ref() {
let _ = tx.send(WaylandRequest::Toplevel(ToplevelRequest::Activate(handle))); let _ = tx.send(WaylandRequest::Toplevel(ToplevelRequest::Activate(handle)));
} }
if let Some(p) = self.popup.take() {
return destroy_popup(p.0);
}
} }
Message::Quit(id) => { Message::Quit(id) => {
if let Some(toplevel_group) = self if let Some(toplevel_group) = self
@ -649,18 +547,7 @@ impl cosmic::Application for CosmicAppList {
} }
Message::DndData(file_path) => { Message::DndData(file_path) => {
if let Some(DndOffer { dock_item, .. }) = self.dnd_offer.as_mut() { if let Some(DndOffer { dock_item, .. }) = self.dnd_offer.as_mut() {
if let Some(di) = std::fs::read_to_string(&file_path).ok().and_then(|input| { if let Some(di) = cosmic::desktop::load_desktop_file(None, &file_path) {
DesktopEntry::decode(&file_path, &input)
.ok()
.map(|de| DesktopInfo {
id: de.id().to_string(),
wm_class: de.startup_wm_class().map(ToString::to_string),
icon: IconSource::from_unknown(de.icon().unwrap_or(de.appid)),
exec: de.exec().unwrap_or_default().to_string(),
name: de.name(None).unwrap_or_default().to_string(),
path: file_path.clone(),
})
}) {
self.item_ctr += 1; self.item_ctr += 1;
*dock_item = Some(DockItem::new(self.item_ctr, Vec::new(), di)); *dock_item = Some(DockItem::new(self.item_ctr, Vec::new(), di));
} }
@ -758,17 +645,14 @@ impl cosmic::Application for CosmicAppList {
info.app_id = format!("Unknown Application {}", self.item_ctr); info.app_id = format!("Unknown Application {}", self.item_ctr);
} }
self.item_ctr += 1; self.item_ctr += 1;
let desktop_info = let desktop_info = load_applications_for_app_ids(
desktop_info_for_app_ids(vec![info.app_id.clone()]) None,
.pop() std::iter::once(&*info.app_id),
.unwrap_or_else(|| DesktopInfo { true,
id: info.app_id.clone(), )
wm_class: None, .into_iter()
icon: IconSource::default(), .next()
exec: String::new(), .unwrap();
name: info.app_id.clone(),
path: PathBuf::new(),
});
self.active_list.push(DockItem { self.active_list.push(DockItem {
id: self.item_ctr, id: self.item_ctr,
toplevels: vec![(handle, info)], toplevels: vec![(handle, info)],
@ -806,23 +690,13 @@ impl cosmic::Application for CosmicAppList {
} }
}, },
WaylandUpdate::ActivationToken { token, exec } => { WaylandUpdate::ActivationToken { token, exec } => {
let mut exec = shlex::Shlex::new(&exec); let mut envs = Vec::new();
let mut cmd = match exec.next() {
Some(cmd) if !cmd.contains('=') => std::process::Command::new(cmd),
_ => return Command::none(),
};
for arg in exec {
// TODO handle "%" args here if necessary?
if !arg.starts_with('%') {
cmd.arg(arg);
}
}
if let Some(token) = token { if let Some(token) = token {
cmd.env("XDG_ACTIVATION_TOKEN", &token); envs.push(("XDG_ACTIVATION_TOKEN", token.clone()));
cmd.env("DESKTOP_STARTUP_ID", &token); envs.push(("DESKTOP_STARTUP_ID", token));
} }
tokio::task::spawn_blocking(|| { tokio::task::spawn_blocking(|| {
crate::process::spawn(cmd); cosmic::desktop::spawn_desktop_exec(exec, envs);
}); });
} }
} }
@ -872,25 +746,29 @@ impl cosmic::Application for CosmicAppList {
} }
// pull back configured items into the favorites list // pull back configured items into the favorites list
self.favorite_list = desktop_info_for_app_ids(self.config.favorites.clone()) self.favorite_list = load_applications_for_app_ids(
.into_iter() None,
.map(|new_dock_item| { self.config.favorites.iter().map(|s| &**s),
if let Some(p) = self true,
.active_list )
.iter() .into_iter()
.position(|dock_item| dock_item.desktop_info.id == new_dock_item.id) .map(|new_dock_item| {
{ if let Some(p) = self
self.active_list.remove(p) .active_list
} else { .iter()
self.item_ctr += 1; .position(|dock_item| dock_item.desktop_info.id == new_dock_item.id)
DockItem { {
id: self.item_ctr, self.active_list.remove(p)
toplevels: Default::default(), } else {
desktop_info: new_dock_item, self.item_ctr += 1;
} DockItem {
id: self.item_ctr,
toplevels: Default::default(),
desktop_info: new_dock_item,
} }
}) }
.collect(); })
.collect();
} }
Message::CloseRequested(id) => { Message::CloseRequested(id) => {
if Some(id) == self.popup.as_ref().map(|p| p.0) { if Some(id) == self.popup.as_ref().map(|p| p.0) {
@ -1051,15 +929,20 @@ impl cosmic::Application for CosmicAppList {
.as_cosmic_icon() .as_cosmic_icon()
.size(self.core.applet.suggested_size().0) .size(self.core.applet.suggested_size().0)
.into() .into()
} else if let Some(( } else if let Some((_popup_id, id)) = self.popup.as_ref().filter(|p| id == p.0) {
_popup_id, let Some(DockItem {
DockItem {
toplevels, toplevels,
desktop_info, desktop_info,
.. ..
}, }) = self
)) = self.popup.as_ref().filter(|p| id == p.0) .favorite_list
{ .iter()
.chain(self.active_list.iter())
.find(|i| i.id == *id)
else {
return iced::widget::text("").into();
};
let is_favorite = self.config.favorites.contains(&desktop_info.id) let is_favorite = self.config.favorites.contains(&desktop_info.id)
|| desktop_info || desktop_info
.wm_class .wm_class
@ -1068,13 +951,20 @@ impl cosmic::Application for CosmicAppList {
let mut content = column![ let mut content = column![
iced::widget::text(&desktop_info.name).horizontal_alignment(Horizontal::Center), iced::widget::text(&desktop_info.name).horizontal_alignment(Horizontal::Center),
cosmic::widget::button(iced::widget::text(fl!("new-window")))
.style(Button::Text)
.on_press(Message::Exec(desktop_info.exec.clone())),
] ]
.padding(8) .padding(8)
.spacing(4) .spacing(4)
.align_items(Alignment::Center); .align_items(Alignment::Center);
if let Some(exec) = desktop_info.exec.clone() {
content = content.push(
cosmic::widget::button(iced::widget::text(fl!("new-window")))
.style(Button::Text)
.on_press(Message::Exec(exec)),
);
content = content.push(divider::horizontal::default());
}
if !toplevels.is_empty() { if !toplevels.is_empty() {
let mut list_col = column![]; let mut list_col = column![];
for (handle, info) in toplevels { for (handle, info) in toplevels {
@ -1089,10 +979,9 @@ impl cosmic::Application for CosmicAppList {
.on_press(Message::Activate(handle.clone())), .on_press(Message::Activate(handle.clone())),
); );
} }
content = content.push(divider::horizontal::default());
content = content.push(list_col); content = content.push(list_col);
content = content.push(divider::horizontal::default());
} }
content = content.push(divider::horizontal::default());
content = content.push(if is_favorite { content = content.push(if is_favorite {
cosmic::widget::button(iced::widget::text(fl!("unfavorite"))) cosmic::widget::button(iced::widget::text(fl!("unfavorite")))
.style(Button::Text) .style(Button::Text)

View file

@ -2,7 +2,6 @@
mod app; mod app;
mod config; mod config;
mod localize; mod localize;
mod process;
mod wayland_handler; mod wayland_handler;
mod wayland_subscription; mod wayland_subscription;

View file

@ -1,31 +0,0 @@
use std::process::{exit, Command, Stdio};
use nix::sys::wait::waitpid;
use nix::unistd::{fork, ForkResult};
/// Performs a double fork with setsid to spawn and detach a command.
pub fn spawn(mut command: Command) {
command
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
unsafe {
match fork() {
Ok(ForkResult::Parent { child }) => {
let _res = waitpid(Some(child), None);
}
Ok(ForkResult::Child) => {
let _res = nix::unistd::setsid();
let _res = command.spawn();
exit(0);
}
Err(why) => {
println!("failed to fork and spawn command: {}", why.desc());
}
}
}
}