config_2014_naga/
lib.rs

1//! Config 2014 Naga Library
2//!
3//! This library provides functionality to remap the 12 side buttons on a
4//! Razer Naga 2014 mouse to configurable keyboard keys on Linux.
5//!
6//! # Example
7//!
8//! ```no_run
9//! use config_2014_naga::{key_map::KeyMapper, run_loop};
10//! use std::sync::{Arc, atomic::AtomicBool};
11//!
12//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
13//! let key_mapper = KeyMapper::default();
14//! let running = Arc::new(AtomicBool::new(true));
15//! run_loop(key_mapper, running)?;
16//! # Ok(())
17//! # }
18//! ```
19
20pub mod event_mapper;
21pub mod input_device;
22pub mod key_map;
23pub mod naga;
24
25use std::error::Error;
26use std::thread;
27use std::time::Duration;
28use std::sync::{
29    Arc,
30    atomic::{AtomicBool, Ordering}
31};
32
33use crate::key_map::KeyMapper;
34
35/// Perform a single attach-and-map cycle.
36///
37/// This is useful for testing or higher-level control loops.
38pub fn run_once(key_mapper: &KeyMapper) -> Result<(), Box<dyn Error>> {
39    let mut device = input_device::create()?;
40    let naga = naga::Naga::new()?;
41    let running = Arc::new(AtomicBool::new(true));
42    event_mapper::map_events(key_mapper.clone(), naga, &mut device, running)?;
43    Ok(())
44}
45
46/// Run the naga remapper loop indefinitely.
47///
48/// # Arguments
49///
50/// * `key_mapper` - Key mapping to use
51/// * `running` - Arc<AtomicBool> flag that allows graceful shutdown
52///
53/// # Notes
54///
55/// - Blocks until `running` is set to false
56/// - Will exit cleanly within ~50ms of setting running to false
57/// - CLI can pass `Arc::new(AtomicBool::new(true))` to run indefinitely
58pub fn run_loop(key_mapper: KeyMapper, running: Arc<AtomicBool>) -> Result<(), Box<dyn Error>> {
59    let mut device = input_device::create()?;
60
61    while running.load(Ordering::SeqCst) {
62        match naga::Naga::new() {
63            Ok(dev) => {
64                #[cfg(debug_assertions)]
65                eprintln!("Attached to naga");
66
67                // Pass running flag so map_events can exit cleanly
68                if let Err(e) = event_mapper::map_events(key_mapper.clone(), dev, &mut device, running.clone()) {
69                    eprintln!("Error mapping events: {}", e);
70                }
71            }
72            Err(_err) => {
73                #[cfg(debug_assertions)]
74                eprintln!("Error looking for naga: {}", _err);
75            }
76        }
77
78        // Only sleep if still running (avoids delay on shutdown)
79        if running.load(Ordering::SeqCst) {
80            thread::sleep(Duration::from_secs(1));
81        }
82    }
83
84    #[cfg(debug_assertions)]
85    eprintln!("run_loop exited cleanly");
86
87    Ok(())
88}
89
90/// Backward-compatible version of `run_loop` for CLI usage
91///
92/// This simply creates a `running` flag that is always true, so the loop
93/// never exits.
94pub fn run_loop_blocking(key_mapper: KeyMapper) -> Result<(), Box<dyn Error>> {
95    let running = Arc::new(AtomicBool::new(true));
96    run_loop(key_mapper, running)
97}