mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
fix: MIME config (#5154)
* fix: MIME config #2487 * fix: path * refactor: enhance logic
This commit is contained in:
@@ -490,17 +490,24 @@ pub fn init_scheme() -> Result<()> {
|
|||||||
}
|
}
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn init_scheme() -> Result<()> {
|
pub fn init_scheme() -> Result<()> {
|
||||||
|
const DESKTOP_FILE: &str = "clash-verge.desktop";
|
||||||
|
|
||||||
|
for scheme in DEEP_LINK_SCHEMES {
|
||||||
|
let handler = format!("x-scheme-handler/{scheme}");
|
||||||
let output = std::process::Command::new("xdg-mime")
|
let output = std::process::Command::new("xdg-mime")
|
||||||
.arg("default")
|
.arg("default")
|
||||||
.arg("clash-verge.desktop")
|
.arg(DESKTOP_FILE)
|
||||||
.arg("x-scheme-handler/clash")
|
.arg(&handler)
|
||||||
.output()?;
|
.output()?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
"failed to set clash scheme, {}",
|
"failed to set {handler}, {}",
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::utils::linux::ensure_mimeapps_entries(DESKTOP_FILE, DEEP_LINK_SCHEMES)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -508,6 +515,9 @@ pub fn init_scheme() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
const DEEP_LINK_SCHEMES: &[&str] = &["clash", "clash-verge"];
|
||||||
|
|
||||||
pub async fn startup_script() -> Result<()> {
|
pub async fn startup_script() -> Result<()> {
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
let script_path = {
|
let script_path = {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::logging;
|
use crate::logging;
|
||||||
use crate::utils::logging::Type;
|
use crate::utils::logging::Type;
|
||||||
|
use anyhow::Result;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -472,6 +473,233 @@ fn extract_nvidia_driver_version(summary: &str) -> Option<&str> {
|
|||||||
.find(|token| token.chars().all(|c| c.is_ascii_digit() || c == '.'))
|
.find(|token| token.chars().all(|c| c.is_ascii_digit() || c == '.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ensure_mimeapps_entries(desktop_file: &str, schemes: &[&str]) -> Result<()> {
|
||||||
|
let Some(path) = mimeapps_list_path() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let original = fs::read_to_string(&path)?;
|
||||||
|
let mut changed = false;
|
||||||
|
|
||||||
|
let mut output_lines: Vec<String> = Vec::new();
|
||||||
|
let mut current_section: Option<SectionKind> = None;
|
||||||
|
let mut section_buffer: Vec<String> = Vec::new();
|
||||||
|
let mut default_present = false;
|
||||||
|
|
||||||
|
for line in original.lines() {
|
||||||
|
let trimmed = line.trim();
|
||||||
|
if trimmed.starts_with('[') {
|
||||||
|
if let Some(kind) = current_section.take() {
|
||||||
|
flush_section(
|
||||||
|
&mut output_lines,
|
||||||
|
&mut section_buffer,
|
||||||
|
desktop_file,
|
||||||
|
schemes,
|
||||||
|
kind,
|
||||||
|
&mut changed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if trimmed.eq_ignore_ascii_case("[Default Applications]") {
|
||||||
|
default_present = true;
|
||||||
|
current_section = Some(SectionKind::DefaultApplications);
|
||||||
|
output_lines.push("[Default Applications]".to_string());
|
||||||
|
continue;
|
||||||
|
} else if trimmed.eq_ignore_ascii_case("[Added Associations]") {
|
||||||
|
current_section = Some(SectionKind::AddedAssociations);
|
||||||
|
output_lines.push("[Added Associations]".to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_section.is_some() {
|
||||||
|
section_buffer.push(line.to_string());
|
||||||
|
} else {
|
||||||
|
output_lines.push(line.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(kind) = current_section.take() {
|
||||||
|
flush_section(
|
||||||
|
&mut output_lines,
|
||||||
|
&mut section_buffer,
|
||||||
|
desktop_file,
|
||||||
|
schemes,
|
||||||
|
kind,
|
||||||
|
&mut changed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !default_present {
|
||||||
|
changed = true;
|
||||||
|
if !output_lines.is_empty() && !output_lines.last().unwrap().is_empty() {
|
||||||
|
output_lines.push(String::new());
|
||||||
|
}
|
||||||
|
output_lines.push("[Default Applications]".to_string());
|
||||||
|
for &scheme in schemes {
|
||||||
|
output_lines.push(format!("x-scheme-handler/{scheme}={desktop_file};"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !changed {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_content = output_lines.join("\n");
|
||||||
|
if !new_content.ends_with('\n') {
|
||||||
|
new_content.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(path, new_content)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mimeapps_list_path() -> Option<PathBuf> {
|
||||||
|
let data_path = env::var_os("XDG_DATA_HOME")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| {
|
||||||
|
env::var_os("HOME").map(PathBuf::from).map(|mut home| {
|
||||||
|
home.push(".local");
|
||||||
|
home.push("share");
|
||||||
|
home
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|mut dir| {
|
||||||
|
dir.push("applications");
|
||||||
|
dir.push("mimeapps.list");
|
||||||
|
dir
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(ref path) = data_path {
|
||||||
|
if path.exists() {
|
||||||
|
return Some(path.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_path = env::var_os("XDG_CONFIG_HOME")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| {
|
||||||
|
env::var_os("HOME").map(PathBuf::from).map(|mut home| {
|
||||||
|
home.push(".config");
|
||||||
|
home
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|mut dir| {
|
||||||
|
dir.push("mimeapps.list");
|
||||||
|
dir
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(ref path) = config_path {
|
||||||
|
if path.exists() {
|
||||||
|
return Some(path.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum SectionKind {
|
||||||
|
DefaultApplications,
|
||||||
|
AddedAssociations,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_section(
|
||||||
|
output: &mut Vec<String>,
|
||||||
|
section: &mut Vec<String>,
|
||||||
|
desktop_file: &str,
|
||||||
|
schemes: &[&str],
|
||||||
|
kind: SectionKind,
|
||||||
|
changed: &mut bool,
|
||||||
|
) {
|
||||||
|
let mut seen: HashSet<&str> = HashSet::new();
|
||||||
|
let mut processed: Vec<String> = Vec::with_capacity(section.len());
|
||||||
|
|
||||||
|
for line in section.drain(..) {
|
||||||
|
let trimmed = line.trim();
|
||||||
|
if trimmed.is_empty() || trimmed.starts_with('#') {
|
||||||
|
processed.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some((raw_key, raw_value)) = trimmed.split_once('=') else {
|
||||||
|
processed.push(line);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(scheme) = match_scheme(raw_key.trim(), schemes) {
|
||||||
|
if !seen.insert(scheme) {
|
||||||
|
*changed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix = line
|
||||||
|
.chars()
|
||||||
|
.take_while(|c| c.is_whitespace())
|
||||||
|
.collect::<String>();
|
||||||
|
let mut values: Vec<String> = raw_value
|
||||||
|
.split(';')
|
||||||
|
.filter_map(|value| {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
(!trimmed.is_empty()).then(|| trimmed.to_string())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(pos) = values.iter().position(|value| value == desktop_file) {
|
||||||
|
if pos != 0 {
|
||||||
|
values.remove(pos);
|
||||||
|
values.insert(0, desktop_file.to_string());
|
||||||
|
*changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
values.insert(0, desktop_file.to_string());
|
||||||
|
*changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_line = format!("{prefix}x-scheme-handler/{scheme}=");
|
||||||
|
new_line.push_str(&values.join(";"));
|
||||||
|
new_line.push(';');
|
||||||
|
|
||||||
|
if new_line != line {
|
||||||
|
*changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
processed.push(new_line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
processed.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ensure_all = matches!(
|
||||||
|
kind,
|
||||||
|
SectionKind::DefaultApplications | SectionKind::AddedAssociations
|
||||||
|
);
|
||||||
|
|
||||||
|
if ensure_all {
|
||||||
|
for &scheme in schemes {
|
||||||
|
if !seen.contains(scheme) {
|
||||||
|
processed.push(format!("x-scheme-handler/{scheme}={desktop_file};"));
|
||||||
|
*changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.extend(processed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_scheme<'a>(key: &str, schemes: &'a [&str]) -> Option<&'a str> {
|
||||||
|
if let Some(rest) = key.strip_prefix("x-scheme-handler/") {
|
||||||
|
return schemes.iter().copied().find(|candidate| *candidate == rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
schemes.iter().copied().find(|candidate| *candidate == key)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure_environment() {
|
pub fn configure_environment() {
|
||||||
let session = SessionEnv::gather();
|
let session = SessionEnv::gather();
|
||||||
let overrides = DmabufOverrides::gather();
|
let overrides = DmabufOverrides::gather();
|
||||||
|
|||||||
Reference in New Issue
Block a user