#[cfg_attr(not(windows), allow(dead_code))]
pub(crate) fn mutex_name(app_id: &str) -> String {
format!(r"Local\{app_id}_si_mutex")
}
#[cfg_attr(not(windows), allow(dead_code))]
pub(crate) fn class_name(app_id: &str) -> String {
format!("{app_id}_si_win")
}
#[cfg_attr(windows, allow(dead_code))]
pub(crate) fn socket_path(app_id: &str) -> std::path::PathBuf {
let dir = std::env::var("XDG_RUNTIME_DIR")
.or_else(|_| std::env::var("TMPDIR"))
.unwrap_or_else(|_| "/tmp".to_string());
std::path::PathBuf::from(dir).join(format!("{app_id}_si.sock"))
}
pub(crate) fn encode_argv(argv: &[String]) -> Vec<u8> {
argv.join("\0").into_bytes()
}
pub(crate) fn decode_argv(bytes: &[u8]) -> Vec<String> {
let s = String::from_utf8_lossy(bytes);
if s.is_empty() {
Vec::new()
} else {
s.split('\0').map(|x| x.to_string()).collect()
}
}
#[cfg(not(windows))]
mod unix;
#[cfg(windows)]
mod win;
pub(crate) struct SingleInstance {
pub app_id: String,
pub on_second: Box<dyn FnMut(Vec<String>)>,
}
pub(crate) fn acquire(app_id: &str) -> bool {
#[cfg(windows)]
{
win::acquire(app_id)
}
#[cfg(not(windows))]
{
unix::acquire(app_id)
}
}
pub(crate) fn forward(app_id: &str, argv: &[String]) {
#[cfg(windows)]
{
win::forward(app_id, argv)
}
#[cfg(not(windows))]
{
unix::forward(app_id, argv)
}
}
pub(crate) fn install_listener(
app_id: &str,
main_hwnd: isize,
on_second: Box<dyn FnMut(Vec<String>)>,
) {
#[cfg(windows)]
{
win::install_listener(app_id, main_hwnd, on_second)
}
#[cfg(not(windows))]
{
unix::install_listener(app_id, main_hwnd, on_second)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn argv_round_trip() {
let argv = vec![
"wind_setting.exe".to_string(),
"--page".to_string(),
"input".to_string(),
];
let bytes = encode_argv(&argv);
assert_eq!(decode_argv(&bytes), argv);
}
#[test]
fn decode_empty() {
assert!(decode_argv(&[]).is_empty());
}
#[test]
fn argv_with_protocol_url() {
let argv = vec![
"exe".to_string(),
"windinput://import/theme?url=https://x/a.yaml".to_string(),
];
assert_eq!(decode_argv(&encode_argv(&argv)), argv);
}
#[test]
fn naming_includes_app_id() {
assert_eq!(
mutex_name("wind_setting_dev"),
r"Local\wind_setting_dev_si_mutex"
);
assert_eq!(class_name("wind_setting_dev"), "wind_setting_dev_si_win");
assert!(socket_path("wind_setting_dev")
.to_string_lossy()
.ends_with("wind_setting_dev_si.sock"));
}
}