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}