float-clock-wayland 0.2.1

Always-on-top floating desktop clock widget for Wayland compositors.
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;

/// Checks if running under Hyprland compositor.
pub fn is_hyprland() -> bool {
    std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok()
}

fn try_read_socket(path: PathBuf) -> Option<(i32, i32)> {
    if let Ok(mut stream) = UnixStream::connect(path)
        && stream.write_all(b"cursorpos").is_ok() {
            let mut buf = String::new();
            if stream.read_to_string(&mut buf).is_ok() {
                let parts: Vec<&str> = buf.trim().split(',').collect();
                if parts.len() >= 2
                    && let (Ok(x), Ok(y)) = (
                        parts[0].trim().parse::<i32>(),
                        parts[1].trim().parse::<i32>(),
                    ) {
                        return Some((x, y));
                    }
            }
        }
    None
}

/// Retrieves absolute screen cursor position via Hyprland IPC Unix socket.
/// Returns None if not supported or connection fails. Zero-subprocess design.
pub fn get_absolute_cursor_pos() -> Option<(i32, i32)> {
    let signature = std::env::var("HYPRLAND_INSTANCE_SIGNATURE").ok()?;

    if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
        let path = PathBuf::from(runtime_dir)
            .join("hypr")
            .join(&signature)
            .join(".socket.sock");
        if let Some(pos) = try_read_socket(path) {
            return Some(pos);
        }
    }

    let legacy_path = PathBuf::from(format!("/tmp/hypr/{signature}/.socket.sock"));
    try_read_socket(legacy_path)
}