diff --git a/CHANGELOG.md b/CHANGELOG.md index f745a373..8ba0df49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Flip writing order of effect colour blocks every other block write to try and even out. Previously the bottom rows were always last to be written. +- Add more examples: ball, comet, pulser. +- Refine the keyboard layout grid for GX502. ## [0.9.4] - 2020-05-05 ### Changed diff --git a/aura/examples/ball.rs b/aura/examples/ball.rs new file mode 100644 index 00000000..5a359b14 --- /dev/null +++ b/aura/examples/ball.rs @@ -0,0 +1,94 @@ +use rog_aura::{AuraDbusWriter, GX502Layout, Key, KeyColourArray, KeyLayout}; +use std::collections::LinkedList; + +#[derive(Debug, Clone)] +struct Ball { + position: (i32, i32), + direction: (i32, i32), + trail: LinkedList<(i32, i32)>, +} +impl Ball { + fn new(x: i32, y: i32, trail_len: u32) -> Self { + let mut trail = LinkedList::new(); + for _ in 1..=trail_len { + trail.push_back((x, y)); + } + + Ball { + position: (x, y), + direction: (1, 1), + trail, + } + } + + fn update(&mut self, key_map: &Vec<[Key; 17]>) { + let pos = self.position; + let dir = self.direction; + + if pos.0 + dir.0 > key_map[pos.1 as usize].len() as i32 - 1 || pos.0 + dir.0 < 0 { + self.direction.0 *= -1; + } else if key_map[(pos.1) as usize][(pos.0 + dir.0) as usize] == Key::None { + self.direction.0 *= -1; + } + + if pos.1 + dir.1 > key_map.len() as i32 - 1 || pos.1 + dir.1 < 0 { + self.direction.1 *= -1; + } else if key_map[(pos.1 + dir.1) as usize][(pos.0) as usize] == Key::None { + self.direction.1 *= -1; + } + + self.trail.pop_front(); + self.trail.push_back(self.position); + + self.position.0 += self.direction.0; + self.position.1 += self.direction.1; + + if self.position.0 > key_map[self.position.1 as usize].len() as i32 { + self.position.0 = key_map[self.position.1 as usize].len() as i32 - 1; + } + } +} + +fn main() -> Result<(), Box> { + let mut writer = AuraDbusWriter::new()?; + + let mut colours = KeyColourArray::new(); + + let layout = GX502Layout::default(); + + let mut balls = [Ball::new(2, 1, 8), Ball::new(4, 6, 6), Ball::new(12, 3, 4)]; + + writer.init_effect()?; + + let rows = layout.get_rows(); + loop { + for (n, ball) in balls.iter_mut().enumerate() { + ball.update(rows); + for (i, pos) in ball.trail.iter().enumerate() { + if let Some(c) = colours.key(rows[pos.1 as usize][pos.0 as usize]) { + *c.0 = 0; + *c.1 = 0; + *c.2 = 0; + if n == 0 { + *c.0 = i as u8 * (255 / ball.trail.len() as u8); + } else if n == 1 { + *c.1 = i as u8 * (255 / ball.trail.len() as u8); + } else if n == 2 { + *c.2 = i as u8 * (255 / ball.trail.len() as u8); + } + }; + } + + if let Some(c) = colours.key(rows[ball.position.1 as usize][ball.position.0 as usize]) { + *c.0 = 255; + *c.1 = 255; + *c.2 = 255; + }; + } + + writer.write_colour_block(&colours)?; + + // can change 100 times per second, so need to slow it down + std::thread::sleep(std::time::Duration::from_millis(60)); + } +} diff --git a/aura/examples/comet.rs b/aura/examples/comet.rs new file mode 100644 index 00000000..f1352335 --- /dev/null +++ b/aura/examples/comet.rs @@ -0,0 +1,29 @@ +use rog_aura::{AuraDbusWriter, GX502Layout, KeyColourArray, KeyLayout}; + +fn main() -> Result<(), Box> { + let mut writer = AuraDbusWriter::new()?; + + let layout = GX502Layout::default(); + + writer.init_effect()?; + let rows = layout.get_rows(); + + let mut column = 0; + loop { + let mut key_colours = KeyColourArray::new(); + for row in rows { + if let Some(c) = key_colours.key(row[column as usize]) { + *c.0 = 255; + }; + } + if column == rows[0].len() - 1 { + column = 0 + } else { + column += 1; + } + + writer.write_colour_block(&key_colours)?; + + std::thread::sleep(std::time::Duration::from_millis(250)); + } +} diff --git a/aura/examples/iterate-keys.rs b/aura/examples/iterate-keys.rs index 60cccd9c..152bb728 100644 --- a/aura/examples/iterate-keys.rs +++ b/aura/examples/iterate-keys.rs @@ -11,29 +11,39 @@ fn main() -> Result<(), Box> { loop { for (r, row) in rows.iter().enumerate() { for (k, key) in row.iter().enumerate() { - *key_colours.key(*key).0 = 254; + if let Some(c) = key_colours.key(*key) { + *c.0 = 255; + }; // Last key of previous row if k == 0 { if r == 0 { let k = &rows[rows.len() - 1][rows[rows.len() - 1].len() - 1]; - *key_colours.key(*k).0 = 0; + if let Some(c) = key_colours.key(*k) { + *c.0 = 0; + }; } else { let k = &rows[r - 1][rows[r - 1].len() - 1]; - *key_colours.key(*k).0 = 0; + if let Some(c) = key_colours.key(*k) { + *c.0 = 0; + }; } } else { let k = &rows[r][k - 1]; - *key_colours.key(*k).0 = 0; + if let Some(c) = key_colours.key(*k) { + *c.0 = 0; + }; } - *key_colours.key(Key::Up).0 = 255; - *key_colours.key(Key::Left).0 = 255; - *key_colours.key(Key::Right).0 = 255; - *key_colours.key(Key::Down).0 = 255; + if let Some(c) = key_colours.key(Key::Up) { + *c.0 = 255; + }; + *key_colours.key(Key::Left).unwrap().0 = 255; + *key_colours.key(Key::Right).unwrap().0 = 255; + *key_colours.key(Key::Down).unwrap().0 = 255; - *key_colours.key(Key::W).0 = 255; - *key_colours.key(Key::A).0 = 255; - *key_colours.key(Key::S).0 = 255; - *key_colours.key(Key::D).0 = 255; + *key_colours.key(Key::W).unwrap().0 = 255; + *key_colours.key(Key::A).unwrap().0 = 255; + *key_colours.key(Key::S).unwrap().0 = 255; + *key_colours.key(Key::D).unwrap().0 = 255; writer.write_colour_block(&key_colours)?; std::thread::sleep(std::time::Duration::from_millis(100)); diff --git a/aura/examples/per-key-effect-2.rs b/aura/examples/per-key-effect-2.rs index 5901a0bb..02ada36c 100644 --- a/aura/examples/per-key-effect-2.rs +++ b/aura/examples/per-key-effect-2.rs @@ -9,21 +9,21 @@ fn main() -> Result<(), Box> { loop { let count = 49; for _ in 0..count { - *key_colours.key(Key::ROG).0 += 5; - *key_colours.key(Key::L).0 += 5; - *key_colours.key(Key::I).0 += 5; - *key_colours.key(Key::N).0 += 5; - *key_colours.key(Key::U).0 += 5; - *key_colours.key(Key::X).0 += 5; + *key_colours.key(Key::ROG).unwrap().0 += 5; + *key_colours.key(Key::L).unwrap().0 += 5; + *key_colours.key(Key::I).unwrap().0 += 5; + *key_colours.key(Key::N).unwrap().0 += 5; + *key_colours.key(Key::U).unwrap().0 += 5; + *key_colours.key(Key::X).unwrap().0 += 5; writer.write_colour_block(&key_colours)?; } for _ in 0..count { - *key_colours.key(Key::ROG).0 -= 5; - *key_colours.key(Key::L).0 -= 5; - *key_colours.key(Key::I).0 -= 5; - *key_colours.key(Key::N).0 -= 5; - *key_colours.key(Key::U).0 -= 5; - *key_colours.key(Key::X).0 -= 5; + *key_colours.key(Key::ROG).unwrap().0 -= 5; + *key_colours.key(Key::L).unwrap().0 -= 5; + *key_colours.key(Key::I).unwrap().0 -= 5; + *key_colours.key(Key::N).unwrap().0 -= 5; + *key_colours.key(Key::U).unwrap().0 -= 5; + *key_colours.key(Key::X).unwrap().0 -= 5; writer.write_colour_block(&key_colours)?; } } diff --git a/aura/examples/pulser.rs b/aura/examples/pulser.rs index eb022406..b30aeea9 100644 --- a/aura/examples/pulser.rs +++ b/aura/examples/pulser.rs @@ -1,5 +1,4 @@ -use rog_aura::{AuraDbusWriter, GX502Layout, Key, KeyColourArray, KeyLayout}; -use std::ops::Sub; +use rog_aura::{AuraDbusWriter, GX502Layout, KeyColourArray, KeyLayout}; fn main() -> Result<(), Box> { let mut writer = AuraDbusWriter::new()?; @@ -10,12 +9,14 @@ fn main() -> Result<(), Box> { writer.init_effect()?; let rows = layout.get_rows(); - let mut fade = 50; + let mut fade = 17; let mut flip = false; loop { for row in rows { for (k, key) in row.iter().enumerate() { - *key_colours.key(*key).1 = 255 / fade / (k + 1) as u8; + if let Some(c) = key_colours.key(*key) { + *c.0 = 255 / fade / (k + 1) as u8; + }; } } @@ -27,10 +28,11 @@ fn main() -> Result<(), Box> { } else { flip = !flip; } - } else if fade < 50 { + } else if fade < 17 { fade += 1; } else { flip = !flip; } + std::thread::sleep(std::time::Duration::from_millis(10)); } } diff --git a/aura/src/aura_dbus.rs b/aura/src/aura_dbus.rs index b80d4b65..b771529b 100644 --- a/aura/src/aura_dbus.rs +++ b/aura/src/aura_dbus.rs @@ -3,14 +3,17 @@ use dbus::blocking::BlockingSender; use dbus::channel::Sender; use dbus::{blocking::Connection, Message}; use std::error::Error; -use std::sync::{Arc, Mutex}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; use std::{thread, time::Duration}; /// Simplified way to write a effect block pub struct AuraDbusWriter { connection: Box, block_time: u64, - stop: Arc>, + stop: Arc, } impl AuraDbusWriter { @@ -20,7 +23,7 @@ impl AuraDbusWriter { Ok(AuraDbusWriter { connection: Box::new(connection), block_time: 10, - stop: Arc::new(Mutex::new(false)), + stop: Arc::new(AtomicBool::new(false)), }) } @@ -35,9 +38,7 @@ impl AuraDbusWriter { println!("GOT {:?}", msg); if let Ok(stop) = msg.read1::() { if stop { - if let Ok(mut lock) = stopper.lock() { - *lock = true; - } + stopper.store(true, Ordering::Relaxed); } } true @@ -75,10 +76,8 @@ impl AuraDbusWriter { .append1(&group[10].to_vec()); self.connection.send(msg).unwrap(); thread::sleep(Duration::from_millis(self.block_time)); - if let Ok(lock) = self.stop.try_lock() { - if *lock { - panic!("Go signal to stop!"); - } + if self.stop.load(Ordering::Relaxed) { + panic!("Go signal to stop!"); } Ok(()) } diff --git a/aura/src/builtins.rs b/aura/src/builtins.rs index 6385a51a..2213a7d2 100644 --- a/aura/src/builtins.rs +++ b/aura/src/builtins.rs @@ -60,7 +60,7 @@ impl BuiltInModeBytes { BuiltInModeByte::WideZoomy => &self.widezoomy, _ => return None, }; - return Some(bytes); + Some(bytes) } } impl Default for BuiltInModeBytes { diff --git a/aura/src/fancy.rs b/aura/src/fancy.rs index 2c48bd83..a838039a 100644 --- a/aura/src/fancy.rs +++ b/aura/src/fancy.rs @@ -5,6 +5,7 @@ /// to the keyboard EC. One row controls one group of keys, these keys are not /// necessarily all on the same row of the keyboard, with some splitting between /// two rows. +#[derive(Clone)] pub struct KeyColourArray([[u8; 64]; 11]); impl Default for KeyColourArray { fn default() -> Self { @@ -43,15 +44,16 @@ impl KeyColourArray { #[inline] pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) { - let (rr, gg, bb) = self.key(key); - *rr = r; - *gg = g; - *bb = b; + if let Some((rr, gg, bb)) = self.key(key) { + *rr = r; + *gg = g; + *bb = b; + } } /// Indexes in to `KeyColourArray` at the correct row and column /// to set a series of three bytes to the chosen R,G,B values - pub fn key(&mut self, key: Key) -> (&mut u8, &mut u8, &mut u8) { + pub fn key(&mut self, key: Key) -> Option<(&mut u8, &mut u8, &mut u8)> { // Tuples are indexes in to array let (row, col) = match key { Key::VolDown => (0, 15), @@ -161,14 +163,15 @@ impl KeyColourArray { // Key::Down => (10, 9), Key::Right => (10, 12), + Key::None => return None, }; // LOLOLOLOLOLOLOL! Look it's safe okay unsafe { - ( + Some(( &mut *(&mut self.0[row][col] as *mut u8), &mut *(&mut self.0[row][col + 1] as *mut u8), &mut *(&mut self.0[row][col + 2] as *mut u8), - ) + )) } } @@ -178,7 +181,7 @@ impl KeyColourArray { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum Key { VolUp, VolDown, @@ -277,16 +280,17 @@ pub enum Key { Left, Right, RFn, + None, } pub trait KeyLayout { - fn get_rows(&self) -> &Vec>; + fn get_rows(&self) -> &Vec<[Key; 17]>; } -pub struct GX502Layout(Vec>); +pub struct GX502Layout(Vec<[Key; 17]>); impl KeyLayout for GX502Layout { - fn get_rows(&self) -> &Vec> { + fn get_rows(&self) -> &Vec<[Key; 17]> { &self.0 } } @@ -294,24 +298,45 @@ impl KeyLayout for GX502Layout { impl Default for GX502Layout { fn default() -> Self { GX502Layout(vec![ - vec![Key::VolDown, Key::VolUp, Key::MicMute, Key::ROG], - vec![ + [ + Key::None, + Key::None, + Key::VolDown, + Key::VolUp, + Key::MicMute, + Key::ROG, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + ], + [ Key::Esc, + Key::None, Key::F1, Key::F2, Key::F3, Key::F4, + Key::None, // not sure which key to put here Key::F5, Key::F6, Key::F7, Key::F8, Key::F9, + Key::F9, Key::F10, Key::F11, Key::F12, Key::Del, ], - vec![ + [ Key::Tilde, Key::N1, Key::N2, @@ -330,7 +355,7 @@ impl Default for GX502Layout { Key::BkSpc3, Key::Home, ], - vec![ + [ Key::Tab, Key::Q, Key::W, @@ -345,9 +370,11 @@ impl Default for GX502Layout { Key::LBracket, Key::RBracket, Key::BackSlash, + Key::BackSlash, + Key::BackSlash, Key::PgUp, ], - vec![ + [ Key::Caps, Key::A, Key::S, @@ -360,13 +387,14 @@ impl Default for GX502Layout { Key::L, Key::SemiColon, Key::Quote, - // + Key::Quote, Key::Ret1, Key::Ret2, Key::Ret3, Key::PgDn, ], - vec![ + [ + Key::LShift, Key::LShift, Key::Z, Key::X, @@ -378,28 +406,50 @@ impl Default for GX502Layout { Key::Comma, Key::Period, Key::FwdSlash, + Key::FwdSlash, Key::Rshift1, Key::Rshift2, Key::Rshift3, Key::End, ], - vec![ + [ Key::LCtrl, Key::LFn, - // Key::Meta, Key::LAlt, Key::Space1, Key::Space2, Key::Space3, Key::Space4, + Key::Space4, Key::RAlt, Key::PrtSc, Key::RCtrl, + Key::RCtrl, + Key::Left, Key::Up, + Key::Right, Key::RFn, ], - vec![Key::Left, Key::Down, Key::Right], + [ + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::None, + Key::Left, + Key::Down, + Key::Right, + Key::None, + ], ]) } } diff --git a/aura/src/lib.rs b/aura/src/lib.rs index d1ba0d5b..49ba60da 100644 --- a/aura/src/lib.rs +++ b/aura/src/lib.rs @@ -1,6 +1,6 @@ -pub static DBUS_NAME: &'static str = "org.rogcore.Daemon"; -pub static DBUS_PATH: &'static str = "/org/rogcore/Daemon"; -pub static DBUS_IFACE: &'static str = "org.rogcore.Daemon"; +pub static DBUS_NAME: &str = "org.rogcore.Daemon"; +pub static DBUS_PATH: &str = "/org/rogcore/Daemon"; +pub static DBUS_IFACE: &str = "org.rogcore.Daemon"; pub const LED_MSG_LEN: usize = 17; mod builtins; diff --git a/rog-core/src/core.rs b/rog-core/src/core.rs index 7b4d4b9d..44e04a4b 100644 --- a/rog-core/src/core.rs +++ b/rog-core/src/core.rs @@ -35,7 +35,7 @@ impl RogCore { let mut dev_handle = RogCore::get_device(vendor, product)?; dev_handle.set_active_configuration(0).unwrap_or(()); - let dev_config = dev_handle.device().config_descriptor(0).unwrap(); + let dev_config = dev_handle.device().config_descriptor(0)?; // Interface with outputs let mut interface = 0; for iface in dev_config.interfaces() { @@ -54,7 +54,7 @@ impl RogCore { } } - dev_handle.set_auto_detach_kernel_driver(true).unwrap(); + dev_handle.set_auto_detach_kernel_driver(true)?; dev_handle.claim_interface(interface)?; Ok(RogCore { @@ -98,12 +98,9 @@ impl RogCore { let path = RogCore::get_fan_path()?; let mut file = OpenOptions::new().write(true).open(path)?; file.write_all(format!("{:?}\n", config.fan_mode).as_bytes()) - .map_err(|err| { - error!("Could not write fan mode: {:?}", err); - }) - .unwrap(); + .unwrap_or_else(|err| error!("Could not write to {}, {:?}", path, err)); self.set_pstate_for_fan_mode(FanLevel::from(config.fan_mode), config)?; - info!("Reloaded last saved settings"); + info!("Reloaded fan mode: {:?}", FanLevel::from(config.fan_mode)); Ok(()) } @@ -122,7 +119,9 @@ impl RogCore { n = 0; } info!("Fan mode stepped to: {:#?}", FanLevel::from(n)); - fan_ctrl.write_all(format!("{:?}\n", n).as_bytes())?; + fan_ctrl + .write_all(format!("{:?}\n", config.fan_mode).as_bytes()) + .unwrap_or_else(|err| error!("Could not write to {}, {:?}", path, err)); self.set_pstate_for_fan_mode(FanLevel::from(n), config)?; config.fan_mode = n; config.write(); diff --git a/rog-core/src/daemon.rs b/rog-core/src/daemon.rs index a3f4d2f6..c66f6e72 100644 --- a/rog-core/src/daemon.rs +++ b/rog-core/src/daemon.rs @@ -157,18 +157,16 @@ pub async fn start_daemon() -> Result<(), Box> { } } // Write a colour block - if let Ok(mut effect_lock) = effect.try_lock() { - // Spawn a writer - if let Some(effect) = effect_lock.take() { - if effect.len() == 11 { - let mut config = config.lock().await; - led_writer - .do_command(AuraCommand::WriteEffect(effect), &mut config) - .await - .map_err(|err| warn!("{:?}", err)) - .unwrap(); - time_mark = Instant::now(); - } + let mut effect_lock = effect.lock().await; + if let Some(effect) = effect_lock.take() { + if effect.len() == 11 { + let mut config = config.lock().await; + led_writer + .do_command(AuraCommand::WriteEffect(effect), &mut config) + .await + .map_err(|err| warn!("{:?}", err)) + .unwrap(); + time_mark = Instant::now(); } } } diff --git a/rog-core/src/led_control.rs b/rog-core/src/led_control.rs index 9e34191d..79930628 100644 --- a/rog-core/src/led_control.rs +++ b/rog-core/src/led_control.rs @@ -52,6 +52,7 @@ impl<'d, C> LedWriter<'d, C> where C: rusb::UsbContext, { + #[inline] pub fn new( device_handle: NonNull>, led_endpoint: u8, @@ -140,11 +141,12 @@ where } /// Should only be used if the bytes you are writing are verified correct + #[inline] async fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> { match unsafe { self.handle.as_ref() }.write_interrupt( self.led_endpoint, message, - Duration::from_millis(10), + Duration::from_millis(2), ) { Ok(_) => {} Err(err) => match err { @@ -155,6 +157,7 @@ where Ok(()) } + #[inline] async fn write_array_of_bytes(&self, messages: &[&[u8]]) -> Result<(), AuraError> { for message in messages { self.write_bytes(*message).await?; @@ -168,6 +171,7 @@ where /// Write an effect block /// /// `aura_effect_init` must be called any effect routine, and called only once. + #[inline] async fn write_effect(&mut self, effect: Vec>) -> Result<(), AuraError> { if self.flip_effect_write { for row in effect.iter().rev() { @@ -179,10 +183,12 @@ where } } self.flip_effect_write = !self.flip_effect_write; + let now = std::time::Instant::now(); Ok(()) } /// Used to set a builtin mode and save the settings for it + #[inline] async fn set_and_save(&self, bytes: &[u8], config: &mut Config) -> Result<(), AuraError> { let mode = BuiltInModeByte::from(bytes[3]); // safety pass-through of possible effect write @@ -201,6 +207,7 @@ where } /// Used to set a builtin mode and save the settings for it + #[inline] async fn reload_last_builtin(&self, config: &Config) -> Result<(), AuraError> { let mode_curr = config.current_mode[3]; let mode = config @@ -220,6 +227,7 @@ where /// Select next Aura effect /// /// If the current effect is the last one then the effect selected wraps around to the first. + #[inline] async fn set_builtin(&self, config: &mut Config, index: usize) -> Result<(), AuraError> { let mode_next = config .builtin_modes diff --git a/rog-core/src/lib.rs b/rog-core/src/lib.rs index 1a84daae..d308fc7b 100644 --- a/rog-core/src/lib.rs +++ b/rog-core/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_must_use)] /// Configuration loading, saving mod config; /// The core module which allows writing to LEDs or polling the