sip 0.3.0

Interactive Wayland screen-region selector with slurp-compatible output
Documentation
use std::fs::{File, OpenOptions};
use std::os::fd::AsFd;
use std::path::PathBuf;

use color_eyre::eyre::{Result, WrapErr, eyre};
use rustix::fs::{FlockOperation, flock};

pub struct Lock {
    _file: File,
}

impl Lock {
    pub fn acquire() -> Result<Self> {
        let path = lock_path();
        let file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(false)
            .mode(0o600)
            .open(&path)
            .wrap_err_with(|| format!("open lock file {}", path.display()))?;

        match flock(file.as_fd(), FlockOperation::NonBlockingLockExclusive) {
            Ok(()) => Ok(Self { _file: file }),
            Err(rustix::io::Errno::WOULDBLOCK) => Err(eyre!(
                "another sip instance is already running for this Wayland session"
            )),
            Err(e) => Err(e).wrap_err("acquire session lock"),
        }
    }
}

fn lock_path() -> PathBuf {
    let runtime = std::env::var_os("XDG_RUNTIME_DIR")
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from("/tmp"));
    let display = std::env::var("WAYLAND_DISPLAY").unwrap_or_else(|_| "wayland-0".into());
    runtime.join(format!("sip-{display}.lock"))
}

use std::os::unix::fs::OpenOptionsExt as _;