Bouncy ball example

This commit is contained in:
Luke
2020-05-06 12:31:58 +12:00
parent 497ba61d22
commit e09dd345c0
14 changed files with 277 additions and 85 deletions

View File

@@ -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

94
aura/examples/ball.rs Normal file
View File

@@ -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<dyn std::error::Error>> {
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));
}
}

29
aura/examples/comet.rs Normal file
View File

@@ -0,0 +1,29 @@
use rog_aura::{AuraDbusWriter, GX502Layout, KeyColourArray, KeyLayout};
fn main() -> Result<(), Box<dyn std::error::Error>> {
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));
}
}

View File

@@ -11,29 +11,39 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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));

View File

@@ -9,21 +9,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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)?;
}
}

View File

@@ -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<dyn std::error::Error>> {
let mut writer = AuraDbusWriter::new()?;
@@ -10,12 +9,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
} 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));
}
}

View File

@@ -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<Connection>,
block_time: u64,
stop: Arc<Mutex<bool>>,
stop: Arc<AtomicBool>,
}
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::<bool>() {
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(())
}

View File

@@ -60,7 +60,7 @@ impl BuiltInModeBytes {
BuiltInModeByte::WideZoomy => &self.widezoomy,
_ => return None,
};
return Some(bytes);
Some(bytes)
}
}
impl Default for BuiltInModeBytes {

View File

@@ -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<Vec<Key>>;
fn get_rows(&self) -> &Vec<[Key; 17]>;
}
pub struct GX502Layout(Vec<Vec<Key>>);
pub struct GX502Layout(Vec<[Key; 17]>);
impl KeyLayout for GX502Layout {
fn get_rows(&self) -> &Vec<Vec<Key>> {
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,
],
])
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -157,18 +157,16 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
}
}
// 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();
}
}
}

View File

@@ -52,6 +52,7 @@ impl<'d, C> LedWriter<'d, C>
where
C: rusb::UsbContext,
{
#[inline]
pub fn new(
device_handle: NonNull<DeviceHandle<C>>,
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<Vec<u8>>) -> 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

View File

@@ -1,3 +1,4 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
mod config;
/// The core module which allows writing to LEDs or polling the