Improve for windows

This commit is contained in:
Timon
2026-05-09 19:25:50 +00:00
parent 0ae71a89c5
commit 0dacec4f16
4 changed files with 81 additions and 27 deletions
Generated
+1
View File
@@ -2138,6 +2138,7 @@ name = "graphite-desktop-platform-win"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"graphite-desktop", "graphite-desktop",
"windows 0.62.2",
"windows-registry 0.6.1", "windows-registry 0.6.1",
"winres", "winres",
] ]
+1
View File
@@ -17,6 +17,7 @@ graphite-desktop = { path = "../.." }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
windows-registry = "0.6" windows-registry = "0.6"
windows = { version = "0.62", features = ["Win32_UI_Shell"] }
[target.'cfg(target_os = "windows")'.build-dependencies] [target.'cfg(target_os = "windows")'.build-dependencies]
winres = "0.1" winres = "0.1"
+78 -26
View File
@@ -1,6 +1,7 @@
use std::env; use std::env;
use std::error::Error; use std::path::Path;
use windows::Win32::UI::Shell::{SHCNE_ASSOCCHANGED, SHCNF_IDLIST, SHChangeNotify};
use windows_registry::CURRENT_USER; use windows_registry::CURRENT_USER;
const PROG_ID: &str = "Graphite.Document"; const PROG_ID: &str = "Graphite.Document";
@@ -11,38 +12,89 @@ const MIME_TYPE: &str = "application/graphite+json";
const FILE_EXTENSION: &str = ".graphite"; const FILE_EXTENSION: &str = ".graphite";
const SUPPORTED_EXTENSIONS: &[&str] = &[FILE_EXTENSION, ".svg", ".png", ".jpg", ".jpeg"]; const SUPPORTED_EXTENSIONS: &[&str] = &[FILE_EXTENSION, ".svg", ".png", ".jpg", ".jpeg"];
pub fn register() { pub fn write() {
if let Err(e) = register_inner() { if let Err(e) = FileAssociationWriter::new(&env::current_exe().unwrap())
.document_type(PROG_ID, DOCUMENT_FRIENDLY_NAME)
.application(EXECUTABLE_NAME, APP_FRIENDLY_NAME, SUPPORTED_EXTENSIONS)
.extension(FILE_EXTENSION, PROG_ID, MIME_TYPE)
.write()
{
eprintln!("Failed to register file associations: {e}"); eprintln!("Failed to register file associations: {e}");
} }
} }
fn register_inner() -> Result<(), Box<dyn Error>> { struct FileAssociationWriter {
let exe = env::current_exe()?; open_command: String,
let exe_string = exe.to_string_lossy(); icon_value: String,
let open_command = format!("\"{exe_string}\" \"%1\""); entries: Vec<RegistryEntry>,
let icon_value = format!("{exe_string},0"); }
let prog_id = CURRENT_USER.create(format!("Software\\Classes\\{PROG_ID}"))?; struct RegistryEntry {
prog_id.set_string("", DOCUMENT_FRIENDLY_NAME)?; path: String,
let prog_id_icon = CURRENT_USER.create(format!("Software\\Classes\\{PROG_ID}\\DefaultIcon"))?; name: String,
prog_id_icon.set_string("", &icon_value)?; value: String,
let prog_id_command = CURRENT_USER.create(format!("Software\\Classes\\{PROG_ID}\\shell\\open\\command"))?; }
prog_id_command.set_string("", &open_command)?;
let app_base = format!("Software\\Classes\\Applications\\{EXECUTABLE_NAME}"); impl FileAssociationWriter {
let app = CURRENT_USER.create(&app_base)?; fn new(executable: &Path) -> Self {
app.set_string("FriendlyAppName", APP_FRIENDLY_NAME)?; let exe_string = executable.to_string_lossy();
let app_command = CURRENT_USER.create(format!("{app_base}\\shell\\open\\command"))?; Self {
app_command.set_string("", &open_command)?; open_command: format!("\"{exe_string}\" \"%1\""),
let supported = CURRENT_USER.create(format!("{app_base}\\SupportedTypes"))?; icon_value: format!("{exe_string},0"),
for extension in SUPPORTED_EXTENSIONS { entries: Vec::new(),
supported.set_string(extension, "")?; }
} }
let extension = CURRENT_USER.create(format!("Software\\Classes\\{FILE_EXTENSION}"))?; fn document_type(mut self, prog_id: &str, friendly_name: &str) -> Self {
extension.set_string("", PROG_ID)?; let base = format!("Software\\Classes\\{prog_id}");
extension.set_string("Content Type", MIME_TYPE)?; self.push(&base, "", friendly_name);
self.push(&format!("{base}\\DefaultIcon"), "", &self.icon_value.clone());
self.push(&format!("{base}\\shell\\open\\command"), "", &self.open_command.clone());
self
}
Ok(()) fn application(mut self, executable_name: &str, friendly_name: &str, supported_extensions: &[&str]) -> Self {
let base = format!("Software\\Classes\\Applications\\{executable_name}");
self.push(&base, "FriendlyAppName", friendly_name);
self.push(&format!("{base}\\shell\\open\\command"), "", &self.open_command.clone());
let supported_path = format!("{base}\\SupportedTypes");
for extension in supported_extensions {
self.push(&supported_path, extension, "");
}
self
}
fn extension(mut self, extension: &str, prog_id: &str, mime_type: &str) -> Self {
let path = format!("Software\\Classes\\{extension}");
self.push(&path, "", prog_id);
self.push(&path, "Content Type", mime_type);
self
}
fn push(&mut self, path: &str, name: &str, value: &str) {
self.entries.push(RegistryEntry {
path: path.to_owned(),
name: name.to_owned(),
value: value.to_owned(),
});
}
fn write(self) -> windows_registry::Result<()> {
let mut changed = false;
for entry in &self.entries {
let key = CURRENT_USER.create(&entry.path)?;
if key.get_string(&entry.name).ok().as_deref() == Some(entry.value.as_str()) {
continue;
}
key.set_string(&entry.name, &entry.value)?;
changed = true;
}
if changed {
unsafe { SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, None, None) };
}
Ok(())
}
} }
+1 -1
View File
@@ -5,7 +5,7 @@ mod file_associations;
fn main() { fn main() {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
file_associations::register(); file_associations::write();
graphite_desktop::start(); graphite_desktop::start();
} }