Compare commits
10 commits
87bb6c6064
...
6a41b5f43f
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a41b5f43f | |||
|
|
5b86851071 | ||
|
|
eead361cca | ||
|
|
904b690c27 | ||
|
|
092d78f7ca | ||
|
|
0e8aa22f97 | ||
|
|
16b5ae74b3 | ||
|
|
8a0b37bd36 | ||
|
|
8d9da92dba | ||
|
|
58a8f2db64 |
24 changed files with 1390 additions and 1337 deletions
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
- [ ] I have disclosed use of any AI generated code in my commit messages.
|
||||||
|
- If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR.
|
||||||
|
- In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment.
|
||||||
|
- [ ] I understand these changes in full and will be able to respond to review comments.
|
||||||
|
- [ ] My change is accurately described in the commit message.
|
||||||
|
- [ ] My contribution is tested and working as described.
|
||||||
|
- [ ] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions.
|
||||||
|
|
||||||
2111
Cargo.lock
generated
2111
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
36
Cargo.toml
36
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pop-launcher"
|
name = "pop-launcher"
|
||||||
version = "1.2.4"
|
version = "1.2.7"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
authors = ["Michael Aaron Murphy <michael@mmurphy.dev>"]
|
authors = ["Michael Aaron Murphy <michael@mmurphy.dev>"]
|
||||||
description = "Library for writing plugins and frontends for pop-launcher"
|
description = "Library for writing plugins and frontends for pop-launcher"
|
||||||
|
|
@ -9,28 +9,28 @@ edition.workspace = true
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["bin", "plugins", "service", "toolkit"]
|
members = ["bin", "plugins", "service", "toolkit"]
|
||||||
resolver = "2"
|
resolver = "3"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
anyhow = "1.0.90"
|
anyhow = "1.0.100"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.226", features = ["derive"] }
|
||||||
serde_json = "1.0.129"
|
serde_json = "1.0.145"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.41"
|
||||||
dirs = "5.0.1"
|
dirs = "6.0.0"
|
||||||
serde_with = "3.11.0"
|
serde_with = "3.14.1"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
flume = "0.11.0"
|
flume = "0.11.1"
|
||||||
toml = "0.8.19"
|
toml = "0.9.7"
|
||||||
regex = "1.11.0"
|
regex = "1.11.2"
|
||||||
ron = "0.8.1"
|
ron = "0.11.0"
|
||||||
tokio = "1.40.0"
|
tokio = "1.47.1"
|
||||||
tokio-stream = "0.1.16"
|
tokio-stream = "0.1.17"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
const_format = "0.2.33"
|
const_format = "0.2.34"
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
@ -49,9 +49,5 @@ features = ["io-std", "io-util"]
|
||||||
workspace = true
|
workspace = true
|
||||||
features = ["io-util"]
|
features = ["io-util"]
|
||||||
|
|
||||||
|
|
||||||
# [patch.crates-io]
|
# [patch.crates-io]
|
||||||
# freedesktop-desktop-entry = { path = "../freedesktop-desktop-entry" }
|
# freedesktop-desktop-entry = { path = "../freedesktop-desktop-entry" }
|
||||||
|
|
||||||
[patch."https://github.com/pop-os/cosmic-protocols"]
|
|
||||||
"cosmic-client-toolkit" = { git = "https://github.com/pop-os//cosmic-protocols" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pop-launcher-bin"
|
name = "pop-launcher-bin"
|
||||||
version = "1.2.4"
|
version = "1.2.7"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
|
|
@ -10,15 +10,15 @@ publish = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pop-launcher-toolkit = { path = "../toolkit" }
|
pop-launcher-toolkit = { path = "../toolkit" }
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
|
tracing-subscriber = { version = "0.3.20", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"fmt",
|
"fmt",
|
||||||
"env-filter",
|
"env-filter",
|
||||||
"chrono",
|
"chrono",
|
||||||
] }
|
] }
|
||||||
tracing-journald = "0.3.0"
|
tracing-journald = "0.3.1"
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
mimalloc = "0.1.43"
|
mimalloc = "0.1.48"
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ async fn main() {
|
||||||
|
|
||||||
fn init_logging(cmd: &str) {
|
fn init_logging(cmd: &str) {
|
||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
use tracing_subscriber::{fmt, EnvFilter};
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
|
|
||||||
let logdir = match dirs::state_dir() {
|
let logdir = match dirs::state_dir() {
|
||||||
Some(dir) => dir.join("pop-launcher/"),
|
Some(dir) => dir.join("pop-launcher/"),
|
||||||
|
|
|
||||||
8
debian/changelog
vendored
8
debian/changelog
vendored
|
|
@ -1,3 +1,11 @@
|
||||||
|
pop-launcher (1.2.6) jammy; urgency=medium
|
||||||
|
|
||||||
|
* Update all cargo dependencies as of 2025-09-25
|
||||||
|
* Fixes for some desktop entries
|
||||||
|
* Fixes for the cosmic beta
|
||||||
|
|
||||||
|
-- Michael Murphy <michael@mmurphy.dev> Thu, 25 Sep 2025 19:55:10 +0200
|
||||||
|
|
||||||
pop-launcher (1.2.4) jammy; urgency=medium
|
pop-launcher (1.2.4) jammy; urgency=medium
|
||||||
|
|
||||||
* Update all cargo dependencies as of 2024-10-18
|
* Update all cargo dependencies as of 2024-10-18
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ edition.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-pidfd = "0.1.4"
|
async-pidfd = "0.1.5"
|
||||||
fork = "0.2.0"
|
fork = "0.2.0"
|
||||||
freedesktop-desktop-entry = "0.6.2"
|
freedesktop-desktop-entry = "0.7.19"
|
||||||
human_format = "1.1.0"
|
human_format = "1.1.0"
|
||||||
human-sort = "0.2.2"
|
human-sort = "0.2.2"
|
||||||
new_mime_guess = "4.0.4"
|
new_mime_guess = "4.0.4"
|
||||||
|
|
@ -18,32 +18,31 @@ regex.workspace = true
|
||||||
ron.workspace = true
|
ron.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
slab = "0.4.9"
|
slab = "0.4.11"
|
||||||
strsim = "0.11.1"
|
strsim = "0.11.1"
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
zbus = "4.4.0"
|
zbus = "5.14.0"
|
||||||
zvariant = "4.2.0"
|
zvariant = "5.10.0"
|
||||||
url = "2.5.2"
|
url = "2.5.7"
|
||||||
sysfs-class = "0.1.3"
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
flume.workspace = true
|
flume.workspace = true
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
bytes = "1.7.2"
|
bytes = "1.10.1"
|
||||||
recently-used-xbel = "1.1.0"
|
recently-used-xbel = "1.1.0"
|
||||||
|
|
||||||
# dependencies cosmic toplevel
|
# dependencies cosmic toplevel
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit" }
|
cctk = { path = "../../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit" }
|
||||||
sctk = { package = "smithay-client-toolkit", version = "0.19.2", features = [
|
sctk = { package = "smithay-client-toolkit", version = "0.20.0", features = [
|
||||||
"calloop",
|
"calloop",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
# dependencies desktop entries
|
# dependencies desktop entries
|
||||||
switcheroo-control = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
switcheroo-control = { path = "../../dbus-settings-bindings/switcheroo-control" }
|
||||||
|
|
||||||
[dependencies.reqwest]
|
[dependencies.reqwest]
|
||||||
version = "0.12.8"
|
version = "0.12.23"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["rustls-tls"]
|
features = ["rustls-tls"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ async fn qcalc(regex: &mut Regex, expression: &str, decimal_comma: bool) -> Opti
|
||||||
String::from("qalc command is not installed")
|
String::from("qalc command is not installed")
|
||||||
} else {
|
} else {
|
||||||
format!("qalc command failed to spawn: {}", why)
|
format!("qalc command failed to spawn: {}", why)
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,18 @@ use tracing::{debug, error, info, warn};
|
||||||
use crate::desktop_entries::utils::{get_description, is_session_cosmic};
|
use crate::desktop_entries::utils::{get_description, is_session_cosmic};
|
||||||
use crate::send;
|
use crate::send;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::mpsc,
|
|
||||||
future::{select, Either},
|
|
||||||
StreamExt,
|
StreamExt,
|
||||||
|
channel::mpsc,
|
||||||
|
future::{Either, select},
|
||||||
};
|
};
|
||||||
use pop_launcher::{
|
use pop_launcher::{
|
||||||
async_stdin, async_stdout, json_input_stream, IconSource, PluginResponse, PluginSearchResult,
|
IconSource, PluginResponse, PluginSearchResult, Request, async_stdin, async_stdout,
|
||||||
Request,
|
json_input_stream,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::iter;
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
|
||||||
use self::toplevel_handler::{toplevel_handler, ToplevelAction};
|
use self::toplevel_handler::{ToplevelAction, toplevel_handler};
|
||||||
|
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
let mut tx = async_stdout();
|
let mut tx = async_stdout();
|
||||||
|
|
@ -110,7 +109,7 @@ pub async fn main() {
|
||||||
|
|
||||||
struct App<W> {
|
struct App<W> {
|
||||||
locales: Vec<String>,
|
locales: Vec<String>,
|
||||||
desktop_entries: Vec<DesktopEntry<'static>>,
|
desktop_entries: Vec<DesktopEntry>,
|
||||||
ids_to_ignore: Vec<u32>,
|
ids_to_ignore: Vec<u32>,
|
||||||
toplevels: Vec<Box<ToplevelInfo>>,
|
toplevels: Vec<Box<ToplevelInfo>>,
|
||||||
calloop_tx: calloop::channel::Sender<ToplevelAction>,
|
calloop_tx: calloop::channel::Sender<ToplevelAction>,
|
||||||
|
|
@ -125,10 +124,9 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
|
|
||||||
let locales = fde::get_languages_from_env();
|
let locales = fde::get_languages_from_env();
|
||||||
|
|
||||||
let paths = fde::Iter::new(fde::default_paths());
|
let desktop_entries = fde::Iter::new(fde::default_paths())
|
||||||
|
.map(|path| DesktopEntry::from_path(path, Some(&locales)))
|
||||||
let desktop_entries = DesktopEntry::from_paths(paths, &locales)
|
.filter_map(Result::ok)
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
(
|
(
|
||||||
|
|
@ -177,62 +175,49 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search(&mut self, query: &str) {
|
async fn search(&mut self, query: &str) {
|
||||||
|
fn contains_pattern(needle: &str, haystack: &[&str]) -> bool {
|
||||||
|
let needle = needle.to_ascii_lowercase();
|
||||||
|
haystack.iter().all(|h| needle.contains(h))
|
||||||
|
}
|
||||||
|
|
||||||
let query = query.to_ascii_lowercase();
|
let query = query.to_ascii_lowercase();
|
||||||
|
let haystack = query.split_ascii_whitespace().collect::<Vec<&str>>();
|
||||||
|
|
||||||
for info in self.toplevels.iter().rev() {
|
for info in &self.toplevels {
|
||||||
let entry = if query.is_empty() {
|
let retain = query.is_empty()
|
||||||
fde::matching::get_best_match(
|
|| contains_pattern(&info.app_id, &haystack)
|
||||||
&[&info.app_id, &info.title],
|
|| contains_pattern(&info.title, &haystack);
|
||||||
&self.desktop_entries,
|
|
||||||
fde::matching::MatchAppIdOptions::default(),
|
if !retain {
|
||||||
)
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let appid = fde::unicase::Ascii::new(info.app_id.as_str());
|
||||||
|
|
||||||
|
let entry = fde::find_app_by_id(&self.desktop_entries, appid)
|
||||||
|
.map(ToOwned::to_owned)
|
||||||
|
.unwrap_or_else(|| fde::DesktopEntry::from_appid(appid.to_string()).to_owned());
|
||||||
|
|
||||||
|
let icon_name = if let Some(icon) = entry.icon() {
|
||||||
|
Cow::Owned(icon.to_owned())
|
||||||
} else {
|
} else {
|
||||||
let lowercase_title = info.title.to_lowercase();
|
Cow::Borrowed("application-x-executable")
|
||||||
let window_words = lowercase_title
|
|
||||||
.split_whitespace()
|
|
||||||
.chain(iter::once(info.app_id.as_str()))
|
|
||||||
.chain(iter::once(info.title.as_str()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
fde::matching::get_best_match(
|
|
||||||
&window_words,
|
|
||||||
&self.desktop_entries,
|
|
||||||
fde::matching::MatchAppIdOptions::default(),
|
|
||||||
)
|
|
||||||
.and_then(|de| {
|
|
||||||
let score =
|
|
||||||
fde::matching::get_entry_score(&query, de, &self.locales, &window_words);
|
|
||||||
|
|
||||||
if score > 0.8 {
|
|
||||||
Some(de)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(de) = entry {
|
let response = PluginResponse::Append(PluginSearchResult {
|
||||||
let icon_name = if let Some(icon) = de.icon() {
|
// XXX protocol id may be re-used later
|
||||||
Cow::Owned(icon.to_owned())
|
id: info.foreign_toplevel.id().protocol_id(),
|
||||||
} else {
|
window: Some((0, info.foreign_toplevel.id().protocol_id())),
|
||||||
Cow::Borrowed("application-x-executable")
|
description: info.title.clone(),
|
||||||
};
|
name: get_description(&entry, &self.locales),
|
||||||
|
icon: Some(IconSource::Name(icon_name)),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
let response = PluginResponse::Append(PluginSearchResult {
|
send(&mut self.tx, response).await;
|
||||||
// XXX protocol id may be re-used later
|
|
||||||
id: info.foreign_toplevel.id().protocol_id(),
|
|
||||||
window: Some((0, info.foreign_toplevel.id().protocol_id())),
|
|
||||||
description: info.title.clone(),
|
|
||||||
name: get_description(de, &self.locales),
|
|
||||||
icon: Some(IconSource::Name(icon_name)),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
send(&mut self.tx, response).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
send(&mut self.tx, PluginResponse::Finished).await;
|
send(&mut self.tx, PluginResponse::Finished).await;
|
||||||
let _ = self.tx.flush().await;
|
let _ = self.tx.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ use cosmic_protocols::{
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
use sctk::registry::{ProvidesRegistryState, RegistryState};
|
use sctk::registry::{ProvidesRegistryState, RegistryState};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use wayland_client::{globals::registry_queue_init, Connection, QueueHandle};
|
use wayland_client::{Connection, QueueHandle, globals::registry_queue_init};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ToplevelAction {
|
pub enum ToplevelAction {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ const EXCLUSIONS: &[&str] = &["GNOME Shell", "Initial Setup"];
|
||||||
struct App<W> {
|
struct App<W> {
|
||||||
current_desktop: Option<Vec<String>>,
|
current_desktop: Option<Vec<String>>,
|
||||||
is_desktop_cosmic: bool,
|
is_desktop_cosmic: bool,
|
||||||
desktop_entries: Vec<DesktopEntry<'static>>,
|
desktop_entries: Vec<DesktopEntry>,
|
||||||
locales: Vec<String>,
|
locales: Vec<String>,
|
||||||
tx: W,
|
tx: W,
|
||||||
gpus: Option<Vec<switcheroo_control::Gpu>>,
|
gpus: Option<Vec<switcheroo_control::Gpu>>,
|
||||||
|
|
@ -69,79 +69,85 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
|
|
||||||
let paths = fde::Iter::new(fde::default_paths());
|
let paths = fde::Iter::new(fde::default_paths());
|
||||||
|
|
||||||
let desktop_entries = DesktopEntry::from_paths(paths, &locales)
|
let desktop_entries = paths
|
||||||
|
.flat_map(|path| DesktopEntry::from_path(path, Some(&locales)))
|
||||||
.filter_map(|de| {
|
.filter_map(|de| {
|
||||||
de.ok().and_then(|de| {
|
// Cache flatpak apps separately from non-flatpak apps.
|
||||||
// Treat Flatpak and system apps differently in the cache so they don't
|
let _flatpak_appid;
|
||||||
// override each other
|
let appid = match de.flatpak() {
|
||||||
let appid = de.flatpak().unwrap_or_else(|| de.appid.as_ref());
|
Some(base_id) => {
|
||||||
if deduplicator.contains(appid) {
|
_flatpak_appid = [base_id, ".", de.appid.as_str()].concat();
|
||||||
return None;
|
_flatpak_appid.as_str()
|
||||||
}
|
}
|
||||||
|
None => de.appid.as_str(),
|
||||||
|
};
|
||||||
|
|
||||||
de.name(&self.locales)?;
|
if deduplicator.contains(appid) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
match de.exec() {
|
de.name(&self.locales)?;
|
||||||
Some(exec) => match exec.split_ascii_whitespace().next() {
|
|
||||||
Some(exec) => {
|
match de.exec() {
|
||||||
if exec == "false" {
|
Some(exec) => match exec.split_ascii_whitespace().next() {
|
||||||
return None;
|
Some(exec) => {
|
||||||
}
|
if exec == "false" {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
None => return None,
|
}
|
||||||
},
|
|
||||||
None => return None,
|
None => return None,
|
||||||
}
|
},
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
// Avoid showing the GNOME Shell entry entirely
|
// Avoid showing the GNOME Shell entry entirely
|
||||||
if de
|
if de
|
||||||
.name(&[] as &[&str])
|
.name(&[] as &[&str])
|
||||||
.map_or(false, |v| EXCLUSIONS.contains(&v.as_ref()))
|
.map_or(false, |v| EXCLUSIONS.contains(&v.as_ref()))
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not show if our desktop is defined in `NotShowIn`.
|
// Do not show if our desktop is defined in `NotShowIn`.
|
||||||
if let Some(not_show_in) = de.not_show_in() {
|
if let Some(not_show_in) = de.not_show_in() {
|
||||||
if let Some(current_desktop) = &self.current_desktop {
|
if let Some(current_desktop) = &self.current_desktop {
|
||||||
if not_show_in.iter().any(|not_show| {
|
if not_show_in.iter().any(|not_show| {
|
||||||
current_desktop
|
current_desktop
|
||||||
.iter()
|
.iter()
|
||||||
.any(|desktop| ¬_show.to_ascii_lowercase() == desktop)
|
.any(|desktop| ¬_show.to_ascii_lowercase() == desktop)
|
||||||
}) {
|
}) {
|
||||||
return None;
|
return None;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do not show if our desktop is not defined in `OnlyShowIn`.
|
// Do not show if our desktop is not defined in `OnlyShowIn`.
|
||||||
if let Some(only_show_in) = de.only_show_in() {
|
if let Some(only_show_in) = de.only_show_in() {
|
||||||
if let Some(current_desktop) = &self.current_desktop {
|
if let Some(current_desktop) = &self.current_desktop {
|
||||||
if !only_show_in.iter().any(|show_in| {
|
if !only_show_in.iter().any(|show_in| {
|
||||||
current_desktop
|
current_desktop
|
||||||
.iter()
|
.iter()
|
||||||
.any(|desktop| &show_in.to_ascii_lowercase() == desktop)
|
.any(|desktop| &show_in.to_ascii_lowercase() == desktop)
|
||||||
}) {
|
}) {
|
||||||
return None;
|
return None;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Treat `OnlyShowIn` as an override otherwise do not show if `NoDisplay` is true
|
}
|
||||||
// Some desktop environments set `OnlyShowIn` and `NoDisplay = true` to
|
// Treat `OnlyShowIn` as an override otherwise do not show if `NoDisplay` is true
|
||||||
// indicate special entries
|
// Some desktop environments set `OnlyShowIn` and `NoDisplay = true` to
|
||||||
else if de.no_display() {
|
// indicate special entries
|
||||||
return None;
|
else if de.no_display() {
|
||||||
}
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Always cache already visited entries to allow overriding entries e.g. by
|
// Always cache already visited entries to allow overriding entries e.g. by
|
||||||
// placing a modified copy in ~/.local/share/applications/
|
// placing a modified copy in ~/.local/share/applications/
|
||||||
//
|
//
|
||||||
// We only do this when we can add an entry to our list, otherwise we risk
|
// We only do this when we can add an entry to our list, otherwise we risk
|
||||||
// ignoring user overrides or valid applications due to shell URL handlers
|
// ignoring user overrides or valid applications due to shell URL handlers
|
||||||
deduplicator.insert(appid.to_owned());
|
deduplicator.insert(appid.to_owned());
|
||||||
|
|
||||||
Some(de)
|
Some(de)
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
@ -213,33 +219,66 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search(&mut self, query: &str) {
|
async fn search(&mut self, query: &str) {
|
||||||
for (id, entry) in self.desktop_entries.iter().enumerate() {
|
let query = query.to_ascii_lowercase();
|
||||||
let score = fde::matching::get_entry_score(query, entry, &self.locales, &[]);
|
|
||||||
|
|
||||||
if score > 0.6 {
|
let &mut Self {
|
||||||
let response = PluginResponse::Append(PluginSearchResult {
|
ref desktop_entries,
|
||||||
id: id as u32,
|
ref locales,
|
||||||
name: entry.name(&self.locales).unwrap_or_default().to_string(),
|
ref mut tx,
|
||||||
description: get_description(entry, &self.locales),
|
..
|
||||||
keywords: entry
|
} = self;
|
||||||
.keywords(&self.locales)
|
|
||||||
.map(|v| v.iter().map(|e| e.to_string()).collect()),
|
|
||||||
icon: entry
|
|
||||||
.icon()
|
|
||||||
.map(|e| Cow::Owned(e.to_string()))
|
|
||||||
.map(IconSource::Name),
|
|
||||||
exec: entry.exec().map(|e| e.to_string()),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
send(&mut self.tx, response).await;
|
let mut items = Vec::with_capacity(16);
|
||||||
|
|
||||||
|
for (id, entry) in desktop_entries.iter().enumerate() {
|
||||||
|
let name = entry.name(locales).unwrap_or_default();
|
||||||
|
let keywords = entry.keywords(locales);
|
||||||
|
items.extend(name.split_ascii_whitespace().map(ToOwned::to_owned));
|
||||||
|
|
||||||
|
if let Some(keywords) = keywords.as_ref() {
|
||||||
|
items.extend(keywords.iter().map(|x| String::from(x.as_ref())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(exec) = entry.exec() {
|
||||||
|
items.push(exec.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
for search_interest in items.drain(..) {
|
||||||
|
let search_interest = search_interest.to_ascii_lowercase();
|
||||||
|
let append = search_interest.starts_with(&*query)
|
||||||
|
|| query
|
||||||
|
.split_ascii_whitespace()
|
||||||
|
.any(|query| search_interest.contains(&*query))
|
||||||
|
|| strsim::jaro_winkler(&*query, &*search_interest) > 0.6;
|
||||||
|
|
||||||
|
if append {
|
||||||
|
let response = PluginResponse::Append(PluginSearchResult {
|
||||||
|
id: id as u32,
|
||||||
|
name: entry.name(&self.locales).unwrap_or_default().to_string(),
|
||||||
|
description: get_description(entry, &self.locales),
|
||||||
|
keywords: entry
|
||||||
|
.keywords(&self.locales)
|
||||||
|
.map(|v| v.iter().map(|e| e.to_string()).collect()),
|
||||||
|
icon: entry
|
||||||
|
.icon()
|
||||||
|
.map(|e| Cow::Owned(e.to_string()))
|
||||||
|
.map(IconSource::Name),
|
||||||
|
exec: entry.exec().map(|e| e.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
send(tx, response).await;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send(&mut self.tx, PluginResponse::Finished).await;
|
send(&mut self.tx, PluginResponse::Finished).await;
|
||||||
|
let _ = self.tx.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn gnome_context(&self, entry: &DesktopEntry<'_>) -> Vec<ContextOption> {
|
async fn gnome_context(&self, entry: &DesktopEntry) -> Vec<ContextOption> {
|
||||||
if self.gpus.is_some() {
|
if self.gpus.is_some() {
|
||||||
vec![ContextOption {
|
vec![ContextOption {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
|
@ -255,7 +294,7 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cosmic_context(&self, entry: &DesktopEntry<'_>) -> Vec<ContextOption> {
|
async fn cosmic_context(&self, entry: &DesktopEntry) -> Vec<ContextOption> {
|
||||||
let mut options = Vec::new();
|
let mut options = Vec::new();
|
||||||
|
|
||||||
if let Some(gpus) = self.gpus.as_ref() {
|
if let Some(gpus) = self.gpus.as_ref() {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub fn path_string(source: &PathSource) -> Cow<'static, str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_description<'a>(de: &'a DesktopEntry<'a>, locales: &[String]) -> String {
|
pub fn get_description<'a>(de: &'a DesktopEntry, locales: &[String]) -> String {
|
||||||
let path_source = PathSource::guess_from(&de.path);
|
let path_source = PathSource::guess_from(&de.path);
|
||||||
|
|
||||||
let desc_source = path_string(&path_source).to_string();
|
let desc_source = path_string(&path_source).to_string();
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ use freedesktop_desktop_entry::{self as fde, get_languages_from_env};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use pop_launcher::*;
|
use pop_launcher::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{convert::TryFrom, fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
use zvariant::{Signature, Type};
|
use zvariant::{Signature, Type};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
pub use config::{load, Config};
|
pub use config::{Config, load};
|
||||||
|
|
||||||
const DEST: &str = "com.System76.PopShell";
|
const DEST: &str = "com.System76.PopShell";
|
||||||
const PATH: &str = "/com/System76/PopShell";
|
const PATH: &str = "/com/System76/PopShell";
|
||||||
|
|
@ -26,9 +26,7 @@ struct Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type for Item {
|
impl Type for Item {
|
||||||
fn signature() -> Signature<'static> {
|
const SIGNATURE: &'static Signature = <((u32, u32), String, String, String)>::SIGNATURE;
|
||||||
Signature::try_from("((uu)sss)").expect("bad dbus signature")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
@ -143,7 +141,7 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
if let Ok(entry) = fde::DesktopEntry::from_str(
|
if let Ok(entry) = fde::DesktopEntry::from_str(
|
||||||
path,
|
path,
|
||||||
&data,
|
&data,
|
||||||
&get_languages_from_env(),
|
Some(&get_languages_from_env()),
|
||||||
) {
|
) {
|
||||||
if let Some(icon) = entry.icon() {
|
if let Some(icon) = entry.icon() {
|
||||||
icon_name = Cow::Owned(icon.to_owned());
|
icon_name = Cow::Owned(icon.to_owned());
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use pop_launcher::*;
|
use pop_launcher::*;
|
||||||
use recently_used_xbel::{parse_file, RecentlyUsed};
|
use recently_used_xbel::{RecentlyUsed, parse_file};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ impl App {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
use fork::{daemon, Fork};
|
use fork::{Fork, daemon};
|
||||||
|
|
||||||
crate::send(&mut self.out, PluginResponse::Close).await;
|
crate::send(&mut self.out, PluginResponse::Close).await;
|
||||||
|
|
||||||
|
|
@ -124,7 +124,7 @@ fn detect_terminal() -> (PathBuf, &'static str) {
|
||||||
freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths())
|
freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths())
|
||||||
.filter_map(|path| {
|
.filter_map(|path| {
|
||||||
std::fs::read_to_string(&path).ok().and_then(|input| {
|
std::fs::read_to_string(&path).ok().and_then(|input| {
|
||||||
DesktopEntry::from_str(&path, &input, &get_languages_from_env())
|
DesktopEntry::from_str(&path, &input, Some(&get_languages_from_env()))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|de| {
|
.and_then(|de| {
|
||||||
if de.no_display()
|
if de.no_display()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use url::Url;
|
||||||
|
|
||||||
use pop_launcher::*;
|
use pop_launcher::*;
|
||||||
|
|
||||||
pub use config::{load, Config, Definition};
|
pub use config::{Config, Definition, load};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,25 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pop-launcher-service"
|
name = "pop-launcher-service"
|
||||||
version = "1.2.4"
|
version = "1.2.7"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
|
||||||
async-oneshot = "0.5.9"
|
async-oneshot = "0.5.9"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.89"
|
||||||
clap = { version = "4.5.27", features = ["derive"] }
|
clap = { version = "4.5.48", features = ["derive"] }
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
futures_codec = "0.4.1"
|
|
||||||
gen-z = "0.1.0"
|
gen-z = "0.1.0"
|
||||||
num_cpus = "1.16.0"
|
num_cpus = "1.17.0"
|
||||||
pop-launcher = { path = "../" }
|
pop-launcher = { path = "../" }
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
ron.workspace = true
|
ron.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde_with = "3.11.0"
|
serde_with = "3.14.1"
|
||||||
slab = "0.4.9"
|
slab = "0.4.11"
|
||||||
strsim = "0.11.1"
|
strsim = "0.11.1"
|
||||||
toml.workspace = true
|
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
flume.workspace = true
|
flume.workspace = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ use crate::priority::Priority;
|
||||||
use crate::recent::RecentUseStorage;
|
use crate::recent::RecentUseStorage;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use flume::{Receiver, Sender};
|
use flume::{Receiver, Sender};
|
||||||
use futures::{future, SinkExt, Stream, StreamExt};
|
use futures::{SinkExt, Stream, StreamExt, future};
|
||||||
use pop_launcher::{
|
use pop_launcher::{
|
||||||
json_input_stream, plugin_paths, ContextOption, IconSource, Indice, PluginResponse,
|
ContextOption, IconSource, Indice, PluginResponse, PluginSearchResult, Request, Response,
|
||||||
PluginSearchResult, Request, Response, SearchResult,
|
SearchResult, json_input_stream, plugin_paths,
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
@ -567,45 +567,6 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
|
||||||
} else {
|
} else {
|
||||||
active_search.sort_by(|a, b| {
|
active_search.sort_by(|a, b| {
|
||||||
// Weight is calculated between 0.0 and 1.0, with higher values being most similar
|
// Weight is calculated between 0.0 and 1.0, with higher values being most similar
|
||||||
fn calculate_weight(meta: &PluginSearchResult, query: &str) -> f64 {
|
|
||||||
let mut weight: f64 = 0.0;
|
|
||||||
|
|
||||||
let name = meta.name.to_ascii_lowercase();
|
|
||||||
let description = meta.description.to_ascii_lowercase();
|
|
||||||
let exec = meta
|
|
||||||
.exec
|
|
||||||
.as_ref()
|
|
||||||
.map(|exec| exec.to_ascii_lowercase())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
for name in name.split_ascii_whitespace().flat_map(|x| x.split('_')) {
|
|
||||||
if name.starts_with(query) {
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if exec.contains(query) {
|
|
||||||
if exec.starts_with(query) {
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
weight = strsim::jaro_winkler(query, &exec) - 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
weight
|
|
||||||
.max(strsim::jaro_winkler(&name, query))
|
|
||||||
.max(strsim::jaro_winkler(&description, query) - 0.1)
|
|
||||||
.max(match meta.keywords.as_ref() {
|
|
||||||
Some(keywords) => keywords
|
|
||||||
.iter()
|
|
||||||
.flat_map(|word| word.split_ascii_whitespace())
|
|
||||||
.fold(0.0, |acc, keyword| {
|
|
||||||
let keyword = keyword.to_ascii_lowercase();
|
|
||||||
acc.max(strsim::jaro_winkler(query, &keyword) - 0.1)
|
|
||||||
}),
|
|
||||||
None => 0.0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let plug1 = match plugins.get(a.0) {
|
let plug1 = match plugins.get(a.0) {
|
||||||
Some(plug) => plug,
|
Some(plug) => plug,
|
||||||
|
|
@ -704,3 +665,120 @@ fn serialize_out<E: serde::Serialize>(output: &mut io::StdoutLock, event: &E) {
|
||||||
let _res = output.write_all(&vec);
|
let _res = output.write_all(&vec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_weight(meta: &PluginSearchResult, query: &str) -> f64 {
|
||||||
|
let mut weight: f64 = 0.0;
|
||||||
|
|
||||||
|
let name = meta.name.to_ascii_lowercase();
|
||||||
|
let description = meta.description.to_ascii_lowercase();
|
||||||
|
let exec = meta
|
||||||
|
.exec
|
||||||
|
.as_ref()
|
||||||
|
.map(|exec| exec.to_ascii_lowercase())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
for name in name.split_ascii_whitespace().flat_map(|x| x.split('_')) {
|
||||||
|
if name.starts_with(query) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if exec.contains(query) {
|
||||||
|
if exec.starts_with(query) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
weight = strsim::jaro_winkler(query, &exec) - 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
weight
|
||||||
|
.max(strsim::jaro_winkler(&name, query))
|
||||||
|
.max(match meta.keywords.as_ref() {
|
||||||
|
Some(keywords) => keywords
|
||||||
|
.iter()
|
||||||
|
.flat_map(|word| word.split_ascii_whitespace())
|
||||||
|
.enumerate()
|
||||||
|
.fold(0.0, |acc, (i, keyword)| {
|
||||||
|
let keyword = keyword.to_ascii_lowercase();
|
||||||
|
let mut v = acc.max(strsim::jaro_winkler(query, &keyword) - 0.1);
|
||||||
|
// small decay factor for keywords later in the list.
|
||||||
|
v *= (90. + 10usize.saturating_sub(i) as f64) / 100.;
|
||||||
|
v
|
||||||
|
}),
|
||||||
|
None => 0.0,
|
||||||
|
})
|
||||||
|
// deprioritize description matches the most
|
||||||
|
.max(strsim::jaro_winkler(&description, query) * 0.9 - 0.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use pop_launcher::PluginSearchResult;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_script_calculate_weight() {
|
||||||
|
// Test queries for each of the prompt's entries
|
||||||
|
let entries = vec![
|
||||||
|
(
|
||||||
|
"Enter BIOS",
|
||||||
|
PluginSearchResult {
|
||||||
|
id: 0,
|
||||||
|
name: "Enter BIOS".to_string(),
|
||||||
|
description: "Reboot into BIOS".to_string(),
|
||||||
|
icon: None,
|
||||||
|
window: None,
|
||||||
|
exec: None,
|
||||||
|
keywords: Some(vec![
|
||||||
|
"bios".to_string(),
|
||||||
|
"uefi".to_string(),
|
||||||
|
"reboot".to_string(),
|
||||||
|
"restart".to_string(),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Restart",
|
||||||
|
PluginSearchResult {
|
||||||
|
id: 3,
|
||||||
|
name: "Restart".to_string(),
|
||||||
|
description: "Reboot the system".to_string(),
|
||||||
|
icon: None,
|
||||||
|
window: None,
|
||||||
|
exec: None,
|
||||||
|
keywords: Some(vec![
|
||||||
|
"power".to_string(),
|
||||||
|
"reboot".to_string(),
|
||||||
|
"restart".to_string(),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let query_reboot = "reboot";
|
||||||
|
let weights_reboot: Vec<f64> = entries
|
||||||
|
.iter()
|
||||||
|
.map(|(_, entry)| calculate_weight(entry, query_reboot))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let idx_restart = entries.iter().position(|(n, _)| *n == "Restart").unwrap();
|
||||||
|
let idx_bios = entries
|
||||||
|
.iter()
|
||||||
|
.position(|(n, _)| *n == "Enter BIOS")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
weights_reboot[idx_restart] > weights_reboot[idx_bios],
|
||||||
|
"Restart should be top for 'reboot', then Enter BIOS"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
weights_reboot[idx_restart] >= 0.85,
|
||||||
|
"Restart should be high for 'reboot'"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
weights_reboot[idx_bios] >= 0.85,
|
||||||
|
"Enter BIOS should be high for 'reboot'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
2
service/src/plugins/external/load.rs
vendored
2
service/src/plugins/external/load.rs
vendored
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use crate::PluginConfig;
|
use crate::PluginConfig;
|
||||||
|
|
||||||
use futures::{stream, Stream, StreamExt};
|
use futures::{Stream, StreamExt, stream};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
|
||||||
4
service/src/plugins/external/mod.rs
vendored
4
service/src/plugins/external/mod.rs
vendored
|
|
@ -8,8 +8,8 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc,
|
Arc,
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ use tokio::{
|
||||||
process::{Child, Command},
|
process::{Child, Command},
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
use tracing::{event, Level};
|
use tracing::{Level, event};
|
||||||
|
|
||||||
pub struct ExternalPlugin {
|
pub struct ExternalPlugin {
|
||||||
id: usize,
|
id: usize,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::collections::{hash_map::DefaultHasher, HashMap};
|
use std::collections::{HashMap, hash_map::DefaultHasher};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
const SHORTTERM_CAP: usize = 20;
|
const SHORTTERM_CAP: usize = 20;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pop-launcher-toolkit"
|
name = "pop-launcher-toolkit"
|
||||||
version = "1.2.4"
|
version = "1.2.7"
|
||||||
description = "A wrapper around pop-launcher, pop-launcher-service and pop-launcher-plugins types for writing plugins and frontends for pop-launcher."
|
description = "A wrapper around pop-launcher, pop-launcher-service and pop-launcher-plugins types for writing plugins and frontends for pop-launcher."
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
|
|
@ -10,19 +10,19 @@ edition.workspace = true
|
||||||
pop-launcher-plugins = { path = "../plugins" }
|
pop-launcher-plugins = { path = "../plugins" }
|
||||||
pop-launcher-service = { path = "../service" }
|
pop-launcher-service = { path = "../service" }
|
||||||
pop-launcher = { path = "../" }
|
pop-launcher = { path = "../" }
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.89"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
|
tracing-subscriber = { version = "0.3.20", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"fmt",
|
"fmt",
|
||||||
"env-filter",
|
"env-filter",
|
||||||
] }
|
] }
|
||||||
dirs = "5.0.1"
|
dirs = "6.0.0"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1", features = ["rt"] }
|
tokio = { version = "1", features = ["rt"] }
|
||||||
fork = "0.1.23"
|
fork = "0.2.0"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "man-pages-plugin"
|
name = "man-pages-plugin"
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
// Copyright © 2021 System76
|
// Copyright © 2021 System76
|
||||||
|
|
||||||
use fork::{daemon, Fork};
|
use fork::{Fork, daemon};
|
||||||
use pop_launcher::{Indice, PluginResponse, PluginSearchResult};
|
use pop_launcher::{Indice, PluginResponse, PluginSearchResult};
|
||||||
use pop_launcher_toolkit::plugin_trait::{async_trait, PluginExt};
|
use pop_launcher_toolkit::plugin_trait::{PluginExt, async_trait};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{exit, Command};
|
use std::process::{Command, exit};
|
||||||
|
|
||||||
// This example demonstrate how to write a pop-launcher plugin using the `PluginExt` helper trait.
|
// This example demonstrate how to write a pop-launcher plugin using the `PluginExt` helper trait.
|
||||||
// We are going to build a plugin to display man pages descriptions and open them on activation.
|
// We are going to build a plugin to display man pages descriptions and open them on activation.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use pop_launcher::{async_stdin, async_stdout, json_input_stream, Indice, PluginResponse, Request};
|
use pop_launcher::{Indice, PluginResponse, Request, async_stdin, async_stdout, json_input_stream};
|
||||||
|
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
use pop_launcher_plugins as plugins;
|
use pop_launcher_plugins as plugins;
|
||||||
|
|
@ -122,7 +122,7 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Ok(file) = logfile {
|
if let Ok(file) = logfile {
|
||||||
use tracing_subscriber::{fmt, EnvFilter};
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
fmt()
|
fmt()
|
||||||
.with_env_filter(EnvFilter::from_default_env())
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
.with_writer(file)
|
.with_writer(file)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue