deskcrew 0.1.9

Every coding agent gets its own cursor on your Windows or Mac desktop, for very few tokens.
// Three pretend agents, each working one column of the screen. Nothing underneath is touched.
use crate::overlay::{Overlay, Rect, screen_size};
use std::sync::{Arc, atomic::AtomicBool};
use std::thread;
use std::time::{Duration, Instant};

const AGENTS: [&str; 3] = ["Claude Code", "Cursor", "fx"];

/// Enough randomness to stop the agents looking synchronised.
struct Dice(u64);

impl Dice {
    fn next(&mut self, below: usize) -> usize {
        self.0 ^= self.0 << 13;
        self.0 ^= self.0 >> 7;
        self.0 ^= self.0 << 17;
        (self.0 % below as u64) as usize
    }
}

pub fn run(seconds: u64, linger: u64) {
    let overlay = Overlay::start(Arc::new(AtomicBool::new(false)));
    let (width, height) = screen_size();
    let (cell_w, cell_h) = (width / 3, height / 4);
    let (button_w, button_h) = (width / 10, height / 14);
    let until = Instant::now() + Duration::from_secs(seconds);

    thread::scope(|scope| {
        for (column, name) in AGENTS.iter().enumerate() {
            let overlay = &overlay;
            scope.spawn(move || {
                let mut dice = Dice(0x9E37_79B9_7F4A_7C15 ^ (column as u64 + 1).wrapping_mul(0xBF58_476D_1CE4_E5B9));
                while Instant::now() < until {
                    let cell = (dice.next(3) as i32, dice.next(3) as i32);
                    let x = column as i32 * cell_w + cell.0 * (cell_w - button_w) / 2;
                    let y = height / 8 + cell.1 * cell_h;
                    let target = Rect { x, y, w: button_w, h: button_h };
                    overlay.move_to(name, x + button_w / 2, y + button_h / 2);
                    overlay.press(name, target);
                    thread::sleep(Duration::from_millis(420 + dice.next(600) as u64));
                }
            });
        }
    });

    thread::sleep(Duration::from_secs(linger));
    let stats = overlay.stop();
    println!("{}", stats.summary());
}