clickclack 0.2.0

A mechanical keyboard sound simulator for macOS
#[cfg(target_os = "macos")]
use cocoa::base::nil;
#[cfg(target_os = "macos")]
use cocoa::appkit::{NSApplication, NSApplicationActivationPolicyAccessory};
#[cfg(target_os = "macos")]
use cocoa::foundation::NSAutoreleasePool;

mod audio;
mod input;
mod ui;
mod config;

use anyhow::Result;
use log::{info, error};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::sync::Arc;
use std::io::Write;

static APP_STATE: Lazy<Arc<Mutex<config::Config>>> = Lazy::new(|| {
    Arc::new(Mutex::new(config::Config::load().unwrap_or_default()))
});

fn main() -> Result<()> {
    // Initialize logging with debug level
    env_logger::Builder::from_env(env_logger::Env::default()
        .filter_or("RUST_LOG", "debug"))
        .format(|buf, record| {
            writeln!(buf, "[{}] {}", record.level(), record.args())
        })
        .init();
    info!("Starting ClickClack...");

    #[cfg(target_os = "macos")]
    unsafe {
        let _pool = NSAutoreleasePool::new(nil);
        let app = NSApplication::sharedApplication(nil);
        app.setActivationPolicy_(NSApplicationActivationPolicyAccessory);
    }

    // Initialize assets
    if let Err(e) = ui::tray::ensure_assets_exist() {
        error!("Failed to initialize assets: {}", e);
        return Err(anyhow::anyhow!("Failed to initialize assets: {}", e));
    }
    info!("Assets initialized successfully");

    // Initialize the sound system
    let sound_engine = Arc::new(audio::SoundEngine::new()?);
    info!("Sound engine initialized");

    // Start keyboard listener in a separate thread
    let keyboard_handler = input::KeyboardHandler::new(sound_engine.clone())?;
    keyboard_handler.start()?;
    info!("Keyboard handler started");

    // Create the tray icon
    let _tray = ui::TrayIcon::new()?;
    info!("Tray icon created");
    
    // Run the main event loop
    #[cfg(target_os = "macos")]
    unsafe {
        let app = NSApplication::sharedApplication(nil);
        app.run();
    }

    #[cfg(not(target_os = "macos"))]
    loop {
        std::thread::sleep(std::time::Duration::from_millis(100));
    }

    Ok(())
}