Skip to content

Commit a054856

Browse files
committed
feat: add desktop entry actions
1 parent eaa21ff commit a054856

File tree

7 files changed

+111
-19
lines changed

7 files changed

+111
-19
lines changed

src/exec/dbus.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ trait Application {
2323
}
2424

2525
impl DesktopEntry<'_> {
26-
pub(crate) fn dbus_launch(&self, conn: &Connection, uris: &[&str]) -> Result<(), ExecError> {
26+
pub(crate) fn dbus_launch(
27+
&self,
28+
conn: &Connection,
29+
uris: &[&str],
30+
action: Option<String>,
31+
) -> Result<(), ExecError> {
2732
let dbus_path = self.appid.replace('.', "/");
2833
let dbus_path = format!("/{dbus_path}");
2934
let app_proxy = ApplicationProxyBlocking::builder(conn)
@@ -41,10 +46,22 @@ impl DesktopEntry<'_> {
4146
}
4247
}
4348

44-
if !uris.is_empty() {
45-
app_proxy.open(uris, platform_data)?;
46-
} else {
47-
app_proxy.activate(platform_data)?;
49+
match action {
50+
None => {
51+
if !uris.is_empty() {
52+
app_proxy.open(uris, platform_data)?;
53+
} else {
54+
app_proxy.activate(platform_data)?;
55+
}
56+
}
57+
Some(action) => {
58+
let parameters: Vec<OwnedValue> = uris
59+
.iter()
60+
.map(|uri| OwnedValue::from(Str::from(*uri)))
61+
.collect();
62+
println!("{:?}", parameters);
63+
app_proxy.activate_action(&action, parameters.as_slice(), platform_data)?
64+
}
4865
}
4966

5067
Ok(())

src/exec/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ pub enum ExecError<'a> {
2929
#[error("Exec key not found in desktop entry '{0:?}'")]
3030
MissingExecKey(&'a Path),
3131

32+
#[error("Action '{action}' not found for desktop entry '{desktop_entry:?}'")]
33+
ActionNotFound {
34+
action: String,
35+
desktop_entry: &'a Path,
36+
},
37+
38+
#[error("Exec key not found for action :'{action}' in desktop entry '{desktop_entry:?}'")]
39+
ActionExecKeyNotFound {
40+
action: String,
41+
desktop_entry: &'a Path,
42+
},
43+
3244
#[error("Failed to launch aplication via dbus: {0}")]
3345
DBusError(#[from] zbus::Error),
3446
}

src/exec/mod.rs

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,69 @@ pub mod error;
1616
mod graphics;
1717

1818
impl DesktopEntry<'_> {
19+
/// Launch the given desktop entry action either via dbus or via its `Exec` key with the default gpu or
20+
/// the alternative one if available.
21+
pub fn launch_action(&self, action: &str, uris: &[&str]) -> Result<(), ExecError> {
22+
let has_action = self
23+
.actions()
24+
.map(|actions| actions.split(';').any(|act| act == action))
25+
.unwrap_or(false);
26+
27+
if !has_action {
28+
return Err(ExecError::ActionNotFound {
29+
action: action.to_string(),
30+
desktop_entry: self.path,
31+
});
32+
}
33+
34+
match Connection::session() {
35+
Ok(conn) => {
36+
if self.is_bus_actionable(&conn) {
37+
self.dbus_launch(&conn, uris, Some(action.to_string()))
38+
} else {
39+
self.shell_launch(uris, Some(action.to_string()))
40+
}
41+
}
42+
Err(_) => self.shell_launch(uris, Some(action.to_string())),
43+
}
44+
}
45+
1946
/// Launch the given desktop entry either via dbus or via its `Exec` key with the default gpu or
2047
/// the alternative one if available.
2148
pub fn launch(&self, uris: &[&str]) -> Result<(), ExecError> {
2249
match Connection::session() {
2350
Ok(conn) => {
2451
if self.is_bus_actionable(&conn) {
25-
self.dbus_launch(&conn, uris)
52+
self.dbus_launch(&conn, uris, None)
2653
} else {
27-
self.shell_launch(uris)
54+
self.shell_launch(uris, None)
2855
}
2956
}
30-
Err(_) => self.shell_launch(uris),
57+
Err(_) => self.shell_launch(uris, None),
3158
}
3259
}
3360

34-
fn shell_launch(&self, uris: &[&str]) -> Result<(), ExecError> {
35-
let exec = self.exec();
36-
if exec.is_none() {
37-
return Err(ExecError::MissingExecKey(self.path));
38-
}
61+
fn shell_launch(&self, uris: &[&str], action: Option<String>) -> Result<(), ExecError> {
62+
let exec = match action {
63+
None => {
64+
let exec = self.exec();
65+
if exec.is_none() {
66+
return Err(ExecError::MissingExecKey(self.path));
67+
}
68+
exec.unwrap()
69+
}
70+
Some(action) => {
71+
let exec = self.action_exec(&action);
72+
if exec.is_none() {
73+
return Err(ExecError::ActionExecKeyNotFound {
74+
action,
75+
desktop_entry: self.path,
76+
});
77+
}
3978

40-
let exec = exec.unwrap();
79+
exec.unwrap()
80+
}
81+
};
4182

4283
let mut exec_args = vec![];
4384

@@ -281,12 +322,34 @@ mod test {
281322
let de = DesktopEntry::decode(path.as_path(), &input).unwrap();
282323
let path = std::env::current_dir().unwrap();
283324
let path = path.to_string_lossy();
284-
let path = format!("file:///{path}");
325+
let path = format!("file://{path}");
285326
let result = de.launch(&[path.as_str()]);
286327

287328
assert_that!(result).is_ok();
288329
}
289330

331+
#[test]
332+
#[ignore = "Needs a desktop environment with alacritty installed, run locally only"]
333+
fn should_launch_action() {
334+
let path = PathBuf::from("/usr/share/applications/Alacritty.desktop");
335+
let input = fs::read_to_string(&path).unwrap();
336+
let de = DesktopEntry::decode(path.as_path(), &input).unwrap();
337+
let result = de.launch_action("New", &[]);
338+
339+
assert_that!(result).is_ok();
340+
}
341+
342+
#[test]
343+
#[ignore = "Needs a desktop environment with Nautilus installed, run locally only"]
344+
fn should_launch_action_via_dbus() {
345+
let path = PathBuf::from("/usr/share/applications/org.gnome.Nautilus.desktop");
346+
let input = fs::read_to_string(&path).unwrap();
347+
let de = DesktopEntry::decode(path.as_path(), &input).unwrap();
348+
let result = de.launch_action("new-window", &[]);
349+
350+
assert_that!(result).is_ok();
351+
}
352+
290353
#[test]
291354
fn should_build_command_with_gpu() {
292355
let cmd = with_non_default_gpu(Command::new("glxgears"));

tests/entries/empty-exec.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Exec=
33
Terminal=false
44
Type=Application
5-
Name=Alacritty
5+
Name=NoExecKey

tests/entries/non-terminal-cmd.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Exec=alacritty -e glxgears -info
33
Terminal=false
44
Type=Application
5-
Name=Alacritty
5+
Name=GlxGearNoTerminal

tests/entries/terminal-cmd.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Exec=glxgears -info
33
Terminal=true
44
Type=Application
5-
Name=Alacritty
5+
Name=GlxGearTerminal

tests/entries/unmatched-quotes.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Exec="alacritty -e
33
Terminal=false
44
Type=Application
5-
Name=Alacritty
5+
Name=InvalidCommand

0 commit comments

Comments
 (0)