Skip to content

Commit de89ad8

Browse files
SteveLauCayangweb
andauthored
fix: app launching on Linux (#390)
* feat: custom open() interface * refactor: front-end invocation * refactor: async open() * refactor: use gtk-launch instead * style: fmt --------- Co-authored-by: ayang <473033518@qq.com>
1 parent a5657e6 commit de89ad8

3 files changed

Lines changed: 90 additions & 3 deletions

File tree

src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ pub fn run() {
134134
server::transcription::transcription,
135135
local::application::get_default_search_paths,
136136
local::application::list_app_with_metadata_in,
137+
util::open
137138
])
138139
.setup(|app| {
139140
let registry = SearchSourceRegistry::default();

src-tauri/src/util/mod.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use std::{path::Path, process::Command};
2+
use tauri::{AppHandle, Runtime};
3+
use tauri_plugin_shell::ShellExt;
4+
5+
enum LinuxDesktopEnvironment {
6+
Gnome,
7+
Kde,
8+
}
9+
10+
impl LinuxDesktopEnvironment {
11+
// This impl is based on: https://wiki.archlinux.org/title/Desktop_entries#Usage
12+
fn launch_app_via_desktop_file<P: AsRef<Path>>(&self, file: P) -> Result<(), String> {
13+
let path = file.as_ref();
14+
if !path.try_exists().map_err(|e| e.to_string())? {
15+
return Err(format!("desktop file [{}] does not exist", path.display()));
16+
}
17+
18+
let cmd_output = match self {
19+
Self::Gnome => {
20+
let uri = path
21+
.file_stem()
22+
.expect("the desktop file should contain a file stem part");
23+
24+
Command::new("gtk-launch")
25+
.arg(uri)
26+
.output()
27+
.map_err(|e| e.to_string())?
28+
}
29+
Self::Kde => Command::new("kde-open")
30+
.arg(path)
31+
.output()
32+
.map_err(|e| e.to_string())?,
33+
};
34+
35+
if !cmd_output.status.success() {
36+
return Err(format!(
37+
"failed to launch app via desktop file [{}], underlying command stderr [{}]",
38+
path.display(),
39+
String::from_utf8_lossy(&cmd_output.stderr)
40+
));
41+
}
42+
43+
Ok(())
44+
}
45+
}
46+
47+
fn get_linux_desktop_environment() -> Option<LinuxDesktopEnvironment> {
48+
let de_os_str = std::env::var_os("XDG_CURRENT_DESKTOP")?;
49+
let de_str = de_os_str
50+
.into_string()
51+
.expect("$XDG_CURRENT_DESKTOP should be UTF-8 encoded");
52+
53+
let de = match de_str.as_str() {
54+
"GNOME" => LinuxDesktopEnvironment::Gnome,
55+
"KDE" => LinuxDesktopEnvironment::Kde,
56+
57+
unsupported_de => unimplemented!(
58+
"This desktop environment [{}] has not been supported yet",
59+
unsupported_de
60+
),
61+
};
62+
63+
Some(de)
64+
}
65+
66+
/// Homemade open() function to support open Linux applications via the `.desktop` file.
67+
//
68+
// tauri_plugin_shell::open() is deprecated, but we still use it.
69+
#[allow(deprecated)]
70+
#[tauri::command]
71+
pub async fn open<R: Runtime>(app_handle: AppHandle<R>, path: String) -> Result<(), String> {
72+
if cfg!(target_os = "linux") {
73+
let borrowed_path = Path::new(&path);
74+
if let Some(file_extension) = borrowed_path.extension() {
75+
if file_extension == "desktop" {
76+
let desktop_environment = get_linux_desktop_environment().expect("The Linux OS is running without a desktop, Coco could never run in such a environment");
77+
return desktop_environment.launch_app_via_desktop_file(path);
78+
}
79+
}
80+
}
81+
82+
app_handle
83+
.shell()
84+
.open(path, None)
85+
.map_err(|e| e.to_string())
86+
}

src/utils/tauriAdapter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
191191
},
192192

193193
async openExternal(url) {
194-
const { open } = await import("@tauri-apps/plugin-shell");
195-
return open(url);
194+
const { invoke } = await import("@tauri-apps/api/core");
195+
return invoke("open", { path: url });
196196
},
197197

198-
isWindows10: isWindows10,
198+
isWindows10,
199199

200200
async setShadow(enable) {
201201
const { getCurrentWebviewWindow } = await import(

0 commit comments

Comments
 (0)