diff --git a/Cargo.lock b/Cargo.lock index 0aa8b1a..f7e182c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -843,6 +843,7 @@ dependencies = [ "rust-embed", "serde", "shlex", + "switcheroo-control", "tokio", "url", "xdg", @@ -862,7 +863,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -870,17 +871,19 @@ dependencies = [ "dirs 5.0.1", "futures-util", "iced_futures", + "known-folders", "notify", "once_cell", "ron", "serde", + "xdg", "zbus", ] [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "quote", "syn 1.0.109", @@ -932,7 +935,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "almost", "cosmic-config", @@ -2196,7 +2199,7 @@ dependencies = [ [[package]] name = "iced" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "iced_accessibility", "iced_core", @@ -2211,7 +2214,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "accesskit", "accesskit_unix", @@ -2220,7 +2223,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "bitflags 1.3.2", "iced_accessibility", @@ -2238,7 +2241,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "futures", "iced_core", @@ -2251,7 +2254,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2274,7 +2277,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2287,7 +2290,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "iced_accessibility", "iced_core", @@ -2299,7 +2302,7 @@ dependencies = [ [[package]] name = "iced_sctk" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "enum-repr", "float-cmp", @@ -2323,7 +2326,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "iced_core", "once_cell", @@ -2333,7 +2336,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "bytemuck", "cosmic-text", @@ -2351,7 +2354,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2371,7 +2374,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "iced_renderer", "iced_runtime", @@ -2571,6 +2574,15 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "kqueue" version = "1.0.8" @@ -2621,7 +2633,7 @@ checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/#912e8b0a4478e67ad4b3ce46e327c70dbe9887d8" +source = "git+https://github.com/pop-os/libcosmic/#bf0508816b7e7098cfcc6eb16ee288207ef0cc31" dependencies = [ "apply", "ashpd", @@ -2632,6 +2644,7 @@ dependencies = [ "css-color", "derive_setters", "fraction", + "freedesktop-desktop-entry", "freedesktop-icons", "iced", "iced_core", @@ -2643,10 +2656,12 @@ dependencies = [ "iced_tiny_skia", "iced_widget", "lazy_static", + "nix 0.27.1", "palette", "rfd", "ron", "serde", + "shlex", "slotmap", "taffy", "thiserror", @@ -4176,6 +4191,14 @@ dependencies = [ "zeno", ] +[[package]] +name = "switcheroo-control" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#5dea929b730460f883935357a1a8fb9736f36f95" +dependencies = [ + "zbus", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 3a64f42..41e585d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Ashley Wulber "] edition = "2021" [dependencies] -libcosmic = { git = "https://github.com/pop-os/libcosmic/", default-features = false, features = ["wayland", "tokio", "single-instance", "dbus-config"]} +libcosmic = { git = "https://github.com/pop-os/libcosmic/", default-features = false, features = ["wayland", "tokio", "single-instance", "dbus-config", "desktop"]} # libcosmic = { path = "../libcosmic", default-features = false, features = ["wayland", "tokio", "single-instance", "dbus-config"] } tokio = { version = "1.17.0", features = ["sync", "rt", "process"] } pretty_env_logger = "0.5" @@ -17,7 +17,7 @@ xdg = "2.4.0" i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.6.4" rust-embed = "6.3.0" -zbus = "3.13" +zbus = "3.14" glob = "0.3.0" freedesktop-desktop-entry = "0.5.0" shlex = "1.1.0" @@ -31,5 +31,7 @@ current_locale = "0.1.1" url = "2.4" nix = "0.26" clap = { version = "4.4.8", features = ["derive"] } +switcheroo-control = { git = "https://github.com/pop-os/dbus-settings-bindings" } + [profile.release] lto = "thin" diff --git a/i18n/en/cosmic_app_library.ftl b/i18n/en/cosmic_app_library.ftl index 793f449..45756d2 100644 --- a/i18n/en/cosmic_app_library.ftl +++ b/i18n/en/cosmic_app_library.ftl @@ -11,6 +11,8 @@ cancel = Cancel search-placeholder = Type to search apps... new-group-placeholder = Folder Name run = Run +run-on = Run on {$gpu} +run-on-default = (Default) remove = Remove create-new = Create new folder delete = Delete diff --git a/src/app.rs b/src/app.rs index 0554c5d..2a4c3b3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::fmt::Debug; use clap::Parser; @@ -7,6 +6,7 @@ use cosmic::app::{ }; use cosmic::cosmic_config::{Config, CosmicConfigEntry}; use cosmic::cosmic_theme::Spacing; +use cosmic::desktop::DesktopEntryData; use cosmic::iced::id::Id; use cosmic::iced::wayland::actions::data_device::ActionInner; use cosmic::iced::wayland::actions::layer_surface::SctkLayerSurfaceSettings; @@ -17,7 +17,8 @@ use cosmic::iced::wayland::layer_surface::{ use cosmic::iced::widget::{column, container, horizontal_rule, row, scrollable, text}; use cosmic::iced::{alignment::Horizontal, executor, Alignment, Length}; use cosmic::iced::{Color, Limits, Subscription}; -use cosmic::iced_core::Rectangle; +use cosmic::iced_core::alignment::Vertical; +use cosmic::iced_core::{Padding, Rectangle}; use cosmic::iced_futures::event::listen_raw; use cosmic::iced_runtime::core::event::wayland::LayerEvent; use cosmic::iced_runtime::core::event::{wayland, PlatformSpecific}; @@ -32,7 +33,7 @@ use cosmic::iced_widget::text_input::focus; use cosmic::iced_widget::{horizontal_space, mouse_area, Container}; use cosmic::theme::{self, Button, TextInput}; use cosmic::widget::button::StyleSheet as ButtonStyleSheet; -use cosmic::widget::icon::{from_name, IconFallback}; +use cosmic::widget::icon::from_name; use cosmic::widget::{button, icon, search_input, text_input, tooltip, Column}; use cosmic::{cctk::sctk, iced, Element, Theme}; use iced::wayland::actions::layer_surface::IcedMargin; @@ -41,8 +42,9 @@ use log::error; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::sync::Arc; +use switcheroo_control::Gpu; -use crate::app_group::{AppLibraryConfig, DesktopAction, DesktopEntryData}; +use crate::app_group::AppLibraryConfig; use crate::fl; use crate::subscriptions::desktop_files::desktop_files; use crate::widgets::application::ApplicationButton; @@ -127,6 +129,24 @@ struct CosmicAppLibrary { scroll_offset: f32, core: Core, group_to_delete: Option, + gpus: Option>, +} + +async fn try_get_gpus() -> Option> { + let connection = zbus::Connection::system().await.ok()?; + let proxy = switcheroo_control::SwitcherooControlProxy::new(&connection) + .await + .ok()?; + + if !proxy.has_dual_gpu().await.ok()? { + return None; + } + + let gpus = proxy.get_gpus().await.ok()?; + if gpus.is_empty() { + return None; + } + Some(gpus) } impl CosmicAppLibrary { @@ -140,6 +160,9 @@ impl CosmicAppLibrary { self.scroll_offset = 0.0; self.cur_group = 0; self.load_apps(); + let fetch_gpus = Command::perform(try_get_gpus(), |gpus| { + cosmic::app::Message::App(Message::GpuUpdate(gpus)) + }); return Command::batch(vec![ text_input::focus(SEARCH_ID.clone()), get_layer_surface(SctkLayerSurfaceSettings { @@ -156,6 +179,7 @@ impl CosmicAppLibrary { }, ..Default::default() }), + fetch_gpus, ]); } } @@ -166,8 +190,8 @@ enum Message { InputChanged(String), Layer(LayerEvent, SurfaceId), Hide, - ActivateApp(usize), - ActivationToken(Option, String), + ActivateApp(usize, Option), + ActivationToken(Option, String, Option), SelectGroup(usize), Delete(usize), ConfirmDelete, @@ -193,6 +217,7 @@ enum Message { LeaveDndOffer, ScrollYOffset(f32), Ignore, + GpuUpdate(Option>), } #[derive(Clone)] @@ -210,61 +235,30 @@ enum MenuAction { DesktopAction(String), } +pub fn menu_button<'a, Message>( + content: impl Into>, +) -> cosmic::widget::Button<'a, Message, cosmic::Renderer> { + cosmic::widget::Button::new(content) + .style(Button::AppletMenu) + .padding(menu_control_padding()) + .width(Length::Fill) +} + +pub fn menu_control_padding() -> Padding { + let theme = cosmic::theme::active(); + let cosmic = theme.cosmic(); + [cosmic.space_xxs(), cosmic.space_m()].into() +} + impl CosmicAppLibrary { pub fn load_apps(&mut self) { let locale = self.locale.as_deref(); - self.all_entries = - freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths()) - .filter_map(|path| { - std::fs::read_to_string(&path).ok().and_then(|input| { - freedesktop_desktop_entry::DesktopEntry::decode(&path, &input) - .ok() - .and_then(|de| { - let name = de - .name(self.locale.as_deref()) - .unwrap_or(Cow::Borrowed(de.appid)) - .to_string(); - let Some(exec) = de.exec() else { - return None; - }; - (!de.no_display()).then(|| { - Arc::new(DesktopEntryData { - id: de.appid.to_string(), - exec: exec.to_string(), - name, - icon: de.icon().map(|s| s.to_string()), - path: path.clone(), - categories: de.categories().unwrap_or_default().to_string(), - desktop_actions: de - .actions() - .map(|actions| { - actions - .split(';') - .filter_map(|action| { - let name = de.action_entry_localized( - action, "Name", locale, - ); - let exec = de.action_entry(action, "Exec"); - if let (Some(name), Some(exec)) = - (name, exec) - { - Some(DesktopAction { - name: name.to_string(), - exec: exec.to_string(), - }) - } else { - None - } - }) - .collect_vec() - }) - .unwrap_or_default(), - }) - }) - }) - }) - }) - .collect(); + self.all_entries = cosmic::desktop::load_applications_filtered(locale, |entry| { + entry.exec().is_some() && !entry.no_display() + }) + .into_iter() + .map(Arc::new) + .collect(); self.entry_path_input = self.config .filtered(self.cur_group, &self.search_value, &self.all_entries); @@ -352,37 +346,32 @@ impl cosmic::Application for CosmicAppLibrary { Message::Hide => { return self.hide(); } - Message::ActivateApp(i) => { + Message::ActivateApp(i, gpu_idx) => { self.edit_name = None; if let Some(de) = self.entry_path_input.get(i) { - let exec = de.exec.clone(); + let exec = de.exec.clone().unwrap(); return request_token( Some(String::from(Self::APP_ID)), Some(WINDOW_ID.clone()), move |token| { - cosmic::app::Message::App(Message::ActivationToken(token, exec)) + cosmic::app::Message::App(Message::ActivationToken( + token, exec, gpu_idx, + )) }, ); } } - Message::ActivationToken(token, exec) => { - let mut exec = shlex::Shlex::new(&exec); - 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); - } - } + Message::ActivationToken(token, exec, gpu_idx) => { + let mut env_vars = Vec::new(); if let Some(token) = token { - cmd.env("XDG_ACTIVATION_TOKEN", token.clone()); - cmd.env("DESKTOP_STARTUP_ID", token); + env_vars.push(("XDG_ACTIVATION_TOKEN".to_string(), token.clone())); + env_vars.push(("DESKTOP_STARTUP_ID".to_string(), token)); + } + if let (Some(gpus), Some(idx)) = (self.gpus.as_ref(), gpu_idx) { + env_vars.extend(gpus[idx].environment.clone().into_iter()); } - tokio::task::spawn_blocking(|| { - crate::process::spawn(cmd); + tokio::task::spawn_blocking(move || { + cosmic::desktop::spawn_desktop_exec(exec, env_vars) }); return self.update(Message::Hide); } @@ -600,6 +589,9 @@ impl cosmic::Application for CosmicAppLibrary { return self.filter_apps(); } } + Message::GpuUpdate(gpus) => { + self.gpus = gpus; + } } Command::none() } @@ -621,27 +613,16 @@ impl cosmic::Application for CosmicAppLibrary { let cosmic = theme.cosmic(); let spacing = &cosmic.spacing; if id == DND_ICON_ID.clone() { - let Some(icon_name) = self + let Some(icon_source) = self .dnd_icon - .and_then(|i| self.entry_path_input.get(i).map(|e| e.icon.as_ref())) + .and_then(|i| self.entry_path_input.get(i).map(|e| &e.icon)) else { return container(horizontal_space(Length::Fixed(1.0))) .width(Length::Fixed(1.0)) .height(Length::Fixed(1.0)) .into(); }; - return icon::from_name( - icon_name - .as_ref() - .map_or("application-default", |s| s.as_str()), - ) - .size(128) - .fallback(Some(IconFallback::Names(vec![ - "application-default".into(), - "application-x-application".into(), - ]))) - .size(32) - .into(); + return icon_source.as_cosmic_icon().size(32).into(); } if id == MENU_ID.clone() { let Some((menu, i)) = self @@ -654,108 +635,60 @@ impl cosmic::Application for CosmicAppLibrary { .height(Length::Fixed(1.0)) .into(); }; - let mut list_column = column![button(text(RUN.clone())) - .style(theme::Button::Custom { - active: Box::new(|focused, theme| { - let mut appearance = theme.active(focused, &theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - hovered: Box::new(|focused, theme| { - let mut appearance = theme.hovered(focused, &theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - disabled: Box::new(|theme| { - let mut appearance = theme.disabled(&theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - pressed: Box::new(|focused, theme| { - let mut appearance = theme.pressed(focused, &theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }) - }) - .on_press(Message::ActivateApp(*i)) - .padding([spacing.space_xxs, spacing.space_m]) - .width(Length::Fill)] - .spacing(8); + + let mut list_column = Vec::new(); + + if let Some(gpus) = self.gpus.as_ref() { + for (j, gpu) in gpus.iter().enumerate() { + let default_idx = if menu.prefers_dgpu { + gpus.iter().position(|gpu| !gpu.default).unwrap_or(0) + } else { + gpus.iter().position(|gpu| gpu.default).unwrap_or(0) + }; + list_column.push( + menu_button(text(format!( + "{} {}", + fl!("run-on", gpu = gpu.name.clone()), + if j == default_idx { + fl!("run-on-default") + } else { + String::new() + } + ))) + .on_press(Message::ActivateApp(*i, Some(j))) + .into(), + ) + } + } else { + list_column.push( + menu_button(text(RUN.clone())) + .on_press(Message::ActivateApp(*i, None)) + .into(), + ); + } if menu.desktop_actions.len() > 0 { - list_column = list_column.push(menu_divider(spacing)); + list_column.push(menu_divider(spacing).into()); for action in menu.desktop_actions.iter() { - list_column = list_column.push( - button(text(&action.name)) - .style(theme::Button::Custom { - active: Box::new(|focused, theme| { - let mut appearance = - theme.active(focused, &theme::Button::Text); - appearance.border_radius = - theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - hovered: Box::new(|focused, theme| { - let mut appearance = - theme.hovered(focused, &theme::Button::Text); - appearance.border_radius = - theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - disabled: Box::new(|theme| { - let mut appearance = theme.disabled(&theme::Button::Text); - appearance.border_radius = - theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - pressed: Box::new(|focused, theme| { - let mut appearance = - theme.pressed(focused, &theme::Button::Text); - appearance.border_radius = - theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - }) - .on_press(Message::SelectAction(MenuAction::DesktopAction( - action.exec.clone(), - ))) - .padding([spacing.space_xxs, spacing.space_m]) - .width(Length::Fill), + list_column.push( + menu_button(text(&action.name)) + .on_press(Message::SelectAction( + MenuAction::DesktopAction(action.exec.clone()).into(), + )) + .into(), ); } - list_column = list_column.push(menu_divider(spacing)); + list_column.push(menu_divider(spacing).into()); } - list_column = list_column.push( - button(text(REMOVE.clone())) - .style(theme::Button::Custom { - active: Box::new(|focused, theme| { - let mut appearance = theme.active(focused, &theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - hovered: Box::new(|focused, theme| { - let mut appearance = theme.hovered(focused, &theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - disabled: Box::new(|theme| { - let mut appearance = theme.disabled(&theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - pressed: Box::new(|focused, theme| { - let mut appearance = theme.pressed(focused, &theme::Button::Text); - appearance.border_radius = theme.cosmic().corner_radii.radius_0.into(); - appearance - }), - }) + list_column.push( + menu_button(text(REMOVE.clone())) .on_press(Message::SelectAction(MenuAction::Remove)) - .padding([spacing.space_xxs, spacing.space_m]) - .width(Length::Fill), + .into(), ); - return container(scrollable(list_column)) + return container(scrollable(Column::with_children(list_column))) + .padding([8, 0]) .style(theme::Container::Custom(Box::new(|theme| { container::Appearance { text_color: Some(theme.cosmic().on_bg_color().into()), @@ -766,12 +699,10 @@ impl cosmic::Application for CosmicAppLibrary { icon_color: Some(theme.cosmic().on_bg_color().into()), } }))) - .padding([ - spacing.space_s, - spacing.space_none, - spacing.space_s, - spacing.space_none, - ]) + .width(Length::Shrink) + .height(Length::Shrink) + .align_x(Horizontal::Center) + .align_y(Vertical::Top) .into(); } if id == NEW_GROUP_WINDOW_ID.clone() { @@ -985,8 +916,15 @@ impl cosmic::Application for CosmicAppLibrary { spacing, ); if self.menu.is_none() { + let gpu_idx = self.gpus.as_ref().map(|gpus| { + if entry.prefers_dgpu { + gpus.iter().position(|gpu| !gpu.default).unwrap_or(0) + } else { + gpus.iter().position(|gpu| gpu.default).unwrap_or(0) + } + }); b = b - .on_pressed(Message::ActivateApp(i)) + .on_pressed(Message::ActivateApp(i, gpu_idx)) .on_cancel(Message::CancelDrag) .on_finish(Message::FinishDrag) .on_create_dnd_source(Message::StartDrag(i)) @@ -1220,7 +1158,7 @@ impl cosmic::Application for CosmicAppLibrary { }) }) .unwrap_or_default(); - let mut self_ = Self { + let self_ = Self { locale: current_locale::current_locale().ok(), config, core, diff --git a/src/app_group.rs b/src/app_group.rs index 445f270..f87a8b2 100644 --- a/src/app_group.rs +++ b/src/app_group.rs @@ -1,11 +1,9 @@ -use std::borrow::Cow; -use std::path::PathBuf; use std::sync::Arc; use std::vec; use cosmic::cosmic_config::cosmic_config_derive::CosmicConfigEntry; use cosmic::cosmic_config::{self, CosmicConfigEntry}; -use freedesktop_desktop_entry::DesktopEntry; +use cosmic::desktop::DesktopEntryData; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -116,65 +114,6 @@ pub struct AppLibraryConfig { groups: Vec, } -#[derive(Debug, Clone, PartialEq)] -pub struct DesktopAction { - pub name: String, - pub exec: String, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct DesktopEntryData { - pub id: String, - pub exec: String, - pub name: String, - pub icon: Option, - pub path: PathBuf, - pub categories: String, - pub desktop_actions: Vec, -} - -impl TryFrom for DesktopEntryData { - type Error = anyhow::Error; - - fn try_from(path: PathBuf) -> Result { - let input = std::fs::read_to_string(&path)?; - let de = DesktopEntry::decode(&path, &input)?; - let name = de.name(None).unwrap_or(Cow::Borrowed(de.appid)).to_string(); - let Some(exec) = de.exec() else { - anyhow::bail!("No exec found in desktop entry") - }; - - Ok(DesktopEntryData { - id: de.appid.to_string(), - exec: exec.to_string(), - name, - icon: de.icon().map(|icon| icon.to_string()), - categories: de.categories().unwrap_or_default().to_string(), - path: path.clone(), - desktop_actions: de - .actions() - .map(|actions| { - actions - .split(';') - .filter_map(|action| { - let name = de.action_entry_localized(action, "name", None); - let exec = de.action_entry(action, "exec"); - if let (Some(name), Some(exec)) = (name, exec) { - Some(DesktopAction { - name: name.to_string(), - exec: exec.to_string(), - }) - } else { - None - } - }) - .collect() - }) - .unwrap_or_default(), - }) - } -} - impl AppLibraryConfig { pub fn version() -> u64 { 1 diff --git a/src/main.rs b/src/main.rs index b004ef8..f8b5e7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ mod config; mod app; mod app_group; mod localize; -pub mod process; mod subscriptions; mod widgets; diff --git a/src/process.rs b/src/process.rs deleted file mode 100644 index 1f58531..0000000 --- a/src/process.rs +++ /dev/null @@ -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()); - } - } - } -} diff --git a/src/widgets/application.rs b/src/widgets/application.rs index 26e5de8..9798095 100644 --- a/src/widgets/application.rs +++ b/src/widgets/application.rs @@ -15,8 +15,8 @@ use cosmic::iced_core::{ Point, Rectangle, Shell, Widget, }; +use cosmic::desktop::DesktopEntryData; use cosmic::iced_core::widget::{operation::OperationOutputWrapper, tree, Operation, Tree}; -use cosmic::widget::icon::{from_name, IconFallback}; use cosmic::{ iced::widget::{column, text}, theme, @@ -24,7 +24,6 @@ use cosmic::{ }; use crate::app::{DND_ICON_ID, WINDOW_ID}; -use crate::app_group::DesktopEntryData; pub const MIME_TYPE: &str = "text/uri-list"; const DRAG_THRESHOLD: f32 = 25.0; @@ -81,16 +80,10 @@ impl<'a, Message: Clone + 'static> ApplicationButton<'a, Message> { }; let content = button( column![ - cosmic::widget::Icon::from( - from_name(image.as_ref().map_or("application-default", |s| s.as_str())) - .size(128) - .fallback(Some(IconFallback::Names(vec![ - "application-default".into(), - "application-x-application".into(), - ]))) - ) - .width(Length::Fixed(72.0)) - .height(Length::Fixed(72.0)), + image + .as_cosmic_icon() + .width(Length::Fixed(72.0)) + .height(Length::Fixed(72.0)), text(name) .horizontal_alignment(Horizontal::Center) .size(14) @@ -112,7 +105,7 @@ impl<'a, Message: Clone + 'static> ApplicationButton<'a, Message> { } .into(); Self { - path: path.clone(), + path: path.clone().unwrap(), content, on_right_release: Box::new(on_right_release), on_pressed, diff --git a/src/widgets/group.rs b/src/widgets/group.rs index 855ac5d..2cbaf09 100644 --- a/src/widgets/group.rs +++ b/src/widgets/group.rs @@ -11,12 +11,13 @@ use cosmic::iced_core::event::{wayland, PlatformSpecific}; use cosmic::iced_runtime::command::platform_specific; use cosmic::iced_widget::graphics::image::image_rs::EncodableLayout; +use cosmic::iced_core::widget::{operation::OperationOutputWrapper, tree, Operation, Tree}; use cosmic::iced_core::{ event, layout, mouse, overlay, renderer, Alignment, Clipboard, Element, Event, Length, Padding, Point, Rectangle, Shell, Widget, }; -use cosmic::iced_core::widget::{operation::OperationOutputWrapper, tree, Operation, Tree}; +use cosmic::desktop::{load_desktop_file, DesktopEntryData}; use cosmic::widget::container; use cosmic::widget::icon::from_name; use cosmic::{ @@ -25,8 +26,6 @@ use cosmic::{ widget::{button, icon}, }; -use crate::app_group::DesktopEntryData; - use super::application::MIME_TYPE; /// A widget that can be dragged and dropped. @@ -444,7 +443,7 @@ where .ok() .and_then(|s| url::Url::from_str(s).ok()) .and_then(|url| url.to_file_path().ok()) - .and_then(|p| DesktopEntryData::try_from(p).ok()) + .and_then(|p| load_desktop_file(None, p)) { shell.publish(on_finish(data));