Compare commits

...
15 Commits
Author SHA1 Message Date
vextrixesandCopilot Autofix powered by AI 0bb63c2689 Potential fix for code scanning alert no. 1: Workflow does not contain permissions
Rust / build (push) Failing after 0s
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-10-25 21:05:46 +01:00
vextrixes 26c8e1d5da Remove dependency installation from rust.yml
Removed dependency installation step from workflow.
2026-10-17 22:27:04 +02:00
vextrixes 8f76b4cca5 Change runner from ubuntu-latest to self-hosted 2026-10-17 22:07:41 +02:00
vextrixes 771c95b215 small things 2026-09-17 14:42:20 +02:00
vextrixes 85e5bed320 Create LICENSE 2026-07-09 18:03:54 +02:00
vextrixes 58b9216c02 Delete LICENSE.md 2026-07-09 18:01:51 +02:00
vextrixes ccf67f4a88 Disable doctest 2026-07-08 12:46:11 +02:00
vextrixes 400e915bce version bump 2026-07-08 08:31:16 +02:00
vextrixes cb71613d54 updated docs and tests 2026-07-08 08:25:08 +02:00
vextrixes 1509425ca8 Merge pull request #1 from vextrixes/preset-saving
Preset saving
2026-07-08 01:11:18 +02:00
vextrixes d3c233f0e2 Updated cargo check warning 2026-07-08 01:10:34 +02:00
vextrixes bbaa0cb96e improved error handling 2026-07-08 01:07:15 +02:00
vextrixes 5b25575de2 updates to satisfy cargo clippy 2026-07-08 00:51:17 +02:00
vextrixes 77f7b1abfe Added preset file saving and tests 2026-07-08 00:34:25 +02:00
vextrixes 17a8bbe42f Created a preset struct 2026-07-07 22:58:47 +02:00
17 changed files with 624 additions and 73 deletions

No files matched your search

+4 -5
View File
@@ -7,19 +7,18 @@ on:
branches: [ "master" ] branches: [ "master" ]
workflow_dispatch: workflow_dispatch:
permissions:
contents: read
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: self-hosted
steps: steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libudev-dev pkg-config
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build - name: Build
run: cargo build --verbose --all-features run: cargo build --verbose --all-features
+5 -1
View File
@@ -4,12 +4,16 @@ authors = ["vextrixes"]
description = "Simple libary to control the CZC gk1000 keyboard" description = "Simple libary to control the CZC gk1000 keyboard"
repository = "https://github.com/vextrixes/gk1000-controller" repository = "https://github.com/vextrixes/gk1000-controller"
license = "Unlicense" license = "Unlicense"
version = "1.0.2" version = "1.1.0"
edition = "2024" edition = "2024"
[lib]
doctest = false
[dependencies] [dependencies]
hidapi = "2.6.6" hidapi = "2.6.6"
[features] [features]
access_hid_wrapper = [] access_hid_wrapper = []
preset_file_saving = []
fast_controller_experimental = [] fast_controller_experimental = []
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 vextrixes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-24
View File
@@ -1,24 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
+1 -1
View File
@@ -1,4 +1,4 @@
#[derive(Default)] #[derive(Default, PartialEq, Debug, Clone)]
pub struct Color { pub struct Color {
pub red: u8, pub red: u8,
pub green: u8, pub green: u8,
File renamed without changes.
+1
View File
@@ -251,6 +251,7 @@ impl Keymap {
} }
impl Keymap { impl Keymap {
/// Returns an array of colors in the placement that the keyboard supports
pub fn map(&self) -> [[u8; 3]; 144] { pub fn map(&self) -> [[u8; 3]; 144] {
self.map self.map
} }
+8
View File
@@ -2,6 +2,8 @@ mod color;
mod hid_wrapper; mod hid_wrapper;
mod keymap; mod keymap;
mod rgb_controller; mod rgb_controller;
#[cfg(test)]
mod tests;
pub use color::Color; pub use color::Color;
pub use hid_wrapper::HidWrapperError; pub use hid_wrapper::HidWrapperError;
@@ -11,6 +13,12 @@ pub use rgb_controller::effect_info::{
DirectionPolicy, EFFECTS, EffectInfo, HorizontalDirections, VerticalDirections, DirectionPolicy, EFFECTS, EffectInfo, HorizontalDirections, VerticalDirections,
}; };
pub use rgb_controller::effects::Effects; pub use rgb_controller::effects::Effects;
pub use rgb_controller::preset::Preset;
#[cfg(feature = "preset_file_saving")]
mod save_preset;
#[cfg(feature = "preset_file_saving")]
pub use save_preset::{LoadPresetError, SavePresetError};
#[cfg(feature = "access_hid_wrapper")] #[cfg(feature = "access_hid_wrapper")]
pub use hid_wrapper::HidWrapper; pub use hid_wrapper::HidWrapper;
+27 -23
View File
@@ -2,8 +2,8 @@ use crate::Color;
use crate::Effects; use crate::Effects;
use crate::HidWrapperError; use crate::HidWrapperError;
use crate::Keymap; use crate::Keymap;
use crate::Preset;
use crate::hid_wrapper::HidWrapper; use crate::hid_wrapper::HidWrapper;
use crate::rgb_controller::effects::Effect;
pub struct RGBController { pub struct RGBController {
hid_wrapper: HidWrapper, hid_wrapper: HidWrapper,
@@ -29,6 +29,10 @@ impl RGBController {
}) })
} }
/// Returns a boolean to indicate if a device is connected
///
/// /// # Errors
/// ```HidWrapperError::*```
pub fn is_device_connected(&mut self) -> Result<bool, HidWrapperError> { pub fn is_device_connected(&mut self) -> Result<bool, HidWrapperError> {
self.hid_wrapper.is_device_connected() self.hid_wrapper.is_device_connected()
} }
@@ -225,7 +229,7 @@ impl RGBController {
speed: u8, speed: u8,
direction: u8, direction: u8,
keymap: Option<[[u8; 3]; 144]>, keymap: Option<[[u8; 3]; 144]>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.hid_wrapper.open_device()?; self.hid_wrapper.open_device()?;
self.prepare_device()?; self.prepare_device()?;
@@ -264,7 +268,7 @@ impl RGBController {
self.hid_wrapper.close_device(); self.hid_wrapper.close_device();
Ok(Effect { Ok(Preset {
effect, effect,
color, color,
full_color, full_color,
@@ -280,7 +284,7 @@ impl RGBController {
color: Color, color: Color,
brightness: u8, brightness: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Static, Effects::Static,
color, color,
@@ -298,7 +302,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::SingleOff, Effects::SingleOff,
color, color,
@@ -316,7 +320,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::SingleOn, Effects::SingleOn,
color, color,
@@ -334,7 +338,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Glittering, Effects::Glittering,
color, color,
@@ -352,7 +356,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Rain, Effects::Rain,
color, color,
@@ -368,7 +372,7 @@ impl RGBController {
&mut self, &mut self,
brightness: u8, brightness: u8,
speed: u8, speed: u8,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Colorful, Effects::Colorful,
Color::default(), Color::default(),
@@ -386,7 +390,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Breath, Effects::Breath,
color, color,
@@ -402,7 +406,7 @@ impl RGBController {
&mut self, &mut self,
brightness: u8, brightness: u8,
speed: u8, speed: u8,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Spectrum, Effects::Spectrum,
Color::default(), Color::default(),
@@ -420,7 +424,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::CentrifugalWave, Effects::CentrifugalWave,
color, color,
@@ -439,7 +443,7 @@ impl RGBController {
speed: u8, speed: u8,
direction: u8, direction: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::VerticalWave, Effects::VerticalWave,
color, color,
@@ -458,7 +462,7 @@ impl RGBController {
speed: u8, speed: u8,
direction: u8, direction: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::HorizontalWave, Effects::HorizontalWave,
color, color,
@@ -476,7 +480,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Rotating, Effects::Rotating,
color, color,
@@ -494,7 +498,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Explosion, Effects::Explosion,
color, color,
@@ -512,7 +516,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Launch, Effects::Launch,
color, color,
@@ -530,7 +534,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Ripples, Effects::Ripples,
color, color,
@@ -548,7 +552,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Snake, Effects::Snake,
color, color,
@@ -566,7 +570,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Pulse, Effects::Pulse,
color, color,
@@ -584,7 +588,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Tilt, Effects::Tilt,
color, color,
@@ -602,7 +606,7 @@ impl RGBController {
brightness: u8, brightness: u8,
speed: u8, speed: u8,
full_color: Option<bool>, full_color: Option<bool>,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::Shuttle, Effects::Shuttle,
color, color,
@@ -618,7 +622,7 @@ impl RGBController {
&mut self, &mut self,
brightness: u8, brightness: u8,
keymap: Keymap, keymap: Keymap,
) -> Result<Effect, HidWrapperError> { ) -> Result<Preset, HidWrapperError> {
self.set_effect( self.set_effect(
Effects::StaticPerKey, Effects::StaticPerKey,
Color::default(), Color::default(),
+9 -7
View File
@@ -1,15 +1,17 @@
use crate::Effects; use crate::Effects;
#[derive(Clone)]
pub struct EffectInfo<'a> { pub struct EffectInfo<'a> {
name: &'a str, pub name: &'a str,
effect: Effects, pub effect: Effects,
can_set_color: bool, pub can_set_color: bool,
can_set_speed: bool, pub can_set_speed: bool,
can_set_direction: DirectionPolicy, pub can_set_direction: DirectionPolicy,
can_set_full_color: bool, pub can_set_full_color: bool,
requires_keymap: bool, pub requires_keymap: bool,
} }
#[derive(PartialEq, Clone)]
pub enum DirectionPolicy { pub enum DirectionPolicy {
None, None,
Horizontal01, Horizontal01,
+2 -12
View File
@@ -4,10 +4,10 @@ use Effects::{
VerticalWave, VerticalWave,
}; };
use crate::Color;
/// Effects of the keyboard /// Effects of the keyboard
#[derive(Clone, Default, PartialEq, Debug)]
pub enum Effects { pub enum Effects {
#[default]
Static, Static,
SingleOn, SingleOn,
SingleOff, SingleOff,
@@ -83,13 +83,3 @@ impl Effects {
} }
} }
} }
pub struct Effect {
pub effect: Effects,
pub color: Color,
pub full_color: bool,
pub brightness: u8,
pub speed: u8,
pub direction: u8,
pub keymap: Option<[[u8; 3]; 144]>,
}
@@ -1,3 +1,4 @@
pub mod controller; pub mod controller;
pub mod effect_info; pub mod effect_info;
pub mod effects; pub mod effects;
pub mod preset;
+80
View File
@@ -0,0 +1,80 @@
use crate::Color;
use crate::EFFECTS;
use crate::EffectInfo;
use crate::Effects;
use crate::RGBController;
#[derive(PartialEq, Debug)]
pub struct Preset {
pub effect: Effects,
pub color: Color,
pub full_color: bool,
pub brightness: u8,
pub speed: u8,
pub direction: u8,
pub keymap: Option<[[u8; 3]; 144]>,
}
impl Preset {
pub fn new(
effect: Effects,
color: Color,
full_color: bool,
brightness: u8,
speed: u8,
direction: u8,
keymap: Option<[[u8; 3]; 144]>,
) -> Self {
Self {
effect,
color,
full_color,
brightness: brightness.clamp(0, 16),
speed: speed.clamp(1, 16),
direction: direction.clamp(0, 3),
keymap,
}
}
/// Validates if the preset is a valid preset that can be applied
pub fn is_valid(&self) -> bool {
if self.brightness > 16 || self.direction > 3 || 1 > self.speed || self.speed > 16 {
return false;
}
let effect_info: &EffectInfo = &EFFECTS[usize::from(self.effect.to_u8() - 1)];
if effect_info.requires_keymap && self.keymap.is_none() {
return false;
}
// These are uneded and cause issues
// if effect_info.can_set_full_color && self.full_color{
// return false;
// }
// if effect_info.can_set_direction == DirectionPolicy::None && self.direction != 0{
// return false;
// }
// if effect_info.can_set_color && self.color != Color::default(){
// return false;
// }
true
}
}
impl Default for Preset {
fn default() -> Self {
Self::new(
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
)
}
}
impl Preset {
pub fn apply_to_device(&self, mut controller: RGBController) -> Result<Preset, crate::HidWrapperError>{
controller.set_effect(self.effect.clone(), self.color.clone(), self.full_color, self.brightness, self.speed, self.direction, self.keymap)
}
}
+196
View File
@@ -0,0 +1,196 @@
use crate::{DirectionPolicy, EFFECTS, EffectInfo, Effects, Preset};
use std::{
collections::VecDeque,
fs::File,
io::{Read, Write},
};
#[derive(Debug)]
pub enum SavePresetError {
InvalidPreset,
FileError(std::io::Error),
NoKeymapGiven,
}
#[derive(Debug)]
pub enum LoadPresetError {
InvalidOrCorruptedFile,
OpenFileError(std::io::Error),
}
const MAGIC_NUMBER_1: u8 = 0b10100100;
const MAGIC_NUMBER_2: u8 = 0b00010000;
const VERSION: u8 = 1;
impl Preset {
/// Method to save a preset to a file.
///
/// # Example
/// ```
/// let preset = gk1000_controller::Preset::default();
/// let mut file = std::fs::File::create("/tmp/file.gk1k").expect("Failed to create");
/// preset.save_to_file(&mut file).expect("Failed to Save");
/// ```
/// Make sure you use a function to create a writable, overridable file.
///
/// # Errors
/// If this function fails a `SavePresetError` will be returned
pub fn save_to_file(&self, _file: &mut File) -> Result<(), SavePresetError> {
if !self.is_valid() {
return Err(SavePresetError::InvalidPreset);
}
match _file.write_all(&self._encode_to_buffer()?) {
Err(err) => Err(SavePresetError::FileError(err)),
_ => Ok(()),
}
}
/// Method to save a preset to a buffer.
///
/// This method is not supposed to be used to save presets to a file
///
/// # Example
/// ```
/// let preset = gk1000_controller::Preset::default();
/// if preset.is_valid(){
/// let buffer = preset._encode_to_buffer().expect("Failed to Encode");
/// }
/// ```
///
/// # Errors
/// If this function fails a `SavePresetError` will be returned
pub fn _encode_to_buffer(&self) -> Result<Vec<u8>, SavePresetError> {
let effect_info: EffectInfo = EFFECTS[self.effect.to_u8() as usize - 1].clone();
let mut buffer: Vec<u8> = vec![MAGIC_NUMBER_1, MAGIC_NUMBER_2, VERSION];
buffer
.push(self.effect.to_u8() + self.full_color as u8 * 128 + self.direction * 32); //Effect id, full_color, and direction in a single byte
if !self.full_color && effect_info.can_set_color {
buffer.extend(&self.color.to_array());
}
if effect_info.can_set_speed {
buffer.push(self.speed);
}
buffer.push(self.brightness);
if effect_info.requires_keymap {
for key_color in self.keymap.ok_or(SavePresetError::NoKeymapGiven)?.iter() {
buffer.extend(key_color);
}
}
Ok(buffer)
}
/// Function to load a preset from a file.
///
/// # Example
/// ```
/// let mut file = std::fs::File::open("/tmp/file.gk1k").expect("Failed to open");
/// let preset = gk1000_controller::Preset::load_from_file(&mut file).expect("Failed to load");
/// ```
///
/// # Errors
/// If this function fails a `LoadPresetError` will be returned
pub fn load_from_file(_file: &mut File) -> Result<Self, LoadPresetError> {
let mut buffer: Vec<u8> = vec![];
match _file.read_to_end(&mut buffer) {
Ok(_) => {
let mut buffer: VecDeque<u8> = buffer.into();
Self::_decode_from_buffer(&mut buffer)
}
Err(err) => Err(LoadPresetError::OpenFileError(err)),
}
}
/// Function to load a preset from a buffer.
///
/// This function is not supposed to be used to load presets from a file
///
/// # Example
/// ```
/// let mut buffer: std::collections::VecDeque<u8> = gk1000_controller::Preset::default()._encode_to_buffer().expect("Failed to Save").into();
/// let preset = gk1000_controller::Preset::_decode_from_buffer(&mut buffer).expect("Failed to decode");
/// ```
///
/// # Errors
/// If this function fails a `LoadPresetError` will be returned
pub fn _decode_from_buffer(buffer: &mut VecDeque<u8>) -> Result<Self, LoadPresetError> {
let mut save: Preset = Default::default();
if buffer.pop_front() != Some(MAGIC_NUMBER_1) || buffer.pop_front() != Some(MAGIC_NUMBER_2)
{
return Err(LoadPresetError::InvalidOrCorruptedFile);
}
if buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?
!= VERSION
{
return Err(LoadPresetError::InvalidOrCorruptedFile);
//todo!("TOO BAD, Invalid VERSION")
}
let effect_byte = buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
save.full_color = (effect_byte & 0b1000_0000) != 0;
let direction: u8 = (effect_byte >> 5) & 0b11;
let effect_id: u8 = effect_byte & 0b0001_1111;
save.effect = Effects::from_u8(effect_id).ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
let effect_info: EffectInfo = EFFECTS[save.effect.to_u8() as usize - 1].clone();
if !save.full_color && effect_info.can_set_color {
save.color.red = buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
save.color.green = buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
save.color.blue = buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
}
if effect_info.can_set_direction != DirectionPolicy::None {
save.direction = direction;
}
if effect_info.can_set_speed {
save.speed = buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
}
save.brightness = buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?;
if effect_info.requires_keymap {
let mut keymap_buffer: [[u8; 3]; 144] = [[0; 3]; 144];
#[allow(clippy::needless_range_loop)]
for i in 0..keymap_buffer.len() {
keymap_buffer[i] = [
buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?,
buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?,
buffer
.pop_front()
.ok_or(LoadPresetError::InvalidOrCorruptedFile)?,
];
}
save.keymap = Some(keymap_buffer);
}
Ok(save)
}
}
+30
View File
@@ -0,0 +1,30 @@
mod tests {
use crate::Keymap;
#[test]
fn test_set_all_keys() {
let mut keymap = Keymap::default();
keymap.set_all_keys(crate::Color {
red: 255,
green: 35,
blue: 45,
});
assert_eq!(keymap.map(), [[255, 35, 45]; 144])
}
#[test]
fn test_set_key() {
let mut keymap = Keymap::default();
keymap.set_key(
crate::KeymapKeys::Tab,
crate::Color {
red: 89,
green: 49,
blue: 67,
},
);
let mut array = [[0, 0, 0]; 144];
array[crate::KeymapKeys::Tab.to_usize()] = [89, 49, 67];
assert_eq!(keymap.map(), array)
}
}
+4
View File
@@ -0,0 +1,4 @@
#[cfg(feature = "preset_file_saving")]
mod preset_saving;
mod keymap;
+235
View File
@@ -0,0 +1,235 @@
#[allow(unused_imports)]
mod tests {
use std::collections::VecDeque;
use crate::{Keymap, Preset};
#[test]
fn test_encode_decode() {
let preset = Preset::new(
crate::Effects::Static,
crate::Color {
red: 0,
green: 0,
blue: 0,
},
true,
6,
1,
0,
None,
);
let mut buffer: VecDeque<u8> = preset._encode_to_buffer().expect("error encoding").into();
assert_eq!(
preset,
Preset::_decode_from_buffer(&mut buffer).expect("error decoding")
);
let preset = Preset::new(
crate::Effects::VerticalWave,
crate::Color {
red: 0,
green: 0,
blue: 0,
},
true,
2,
9,
2,
None,
);
let mut buffer: VecDeque<u8> = preset._encode_to_buffer().expect("error encoding").into();
assert_eq!(
preset,
Preset::_decode_from_buffer(&mut buffer).expect("error decoding")
);
let preset = Preset::new(
crate::Effects::HorizontalWave,
crate::Color {
red: 67,
green: 69,
blue: 255,
},
false,
2,
9,
1,
None,
);
let mut buffer: VecDeque<u8> = preset._encode_to_buffer().expect("error encoding").into();
assert_eq!(
preset,
Preset::_decode_from_buffer(&mut buffer).expect("error decoding")
);
let mut keymap: Keymap = Default::default();
keymap.set_all_keys(crate::Color {
red: 255,
green: 9,
blue: 7,
});
keymap.set_key(
crate::KeymapKeys::C,
crate::Color {
red: 111,
green: 222,
blue: 133,
},
);
keymap.set_key(
crate::KeymapKeys::NumPlus,
crate::Color {
red: 112,
green: 223,
blue: 134,
},
);
let preset = Preset::new(
crate::Effects::StaticPerKey,
crate::Color {
red: 0,
green: 0,
blue: 0,
},
false,
16,
1,
0,
Some(keymap.map()),
);
let mut buffer: VecDeque<u8> = preset._encode_to_buffer().expect("error encoding").into();
assert_eq!(
preset,
Preset::_decode_from_buffer(&mut buffer).expect("error decoding")
)
}
#[cfg(target_os = "linux")]
#[test]
fn test_save_load() {
use std::fs::File;
use std::{env, fs};
let preset = Preset::new(
crate::Effects::Static,
crate::Color {
red: 0,
green: 0,
blue: 0,
},
true,
6,
1,
0,
None,
);
preset
.save_to_file(&mut File::create("/tmp/test_file.gk1k").expect("Failed to create file"))
.expect("failed to save");
assert_eq!(
preset,
Preset::load_from_file(
&mut File::open("/tmp/test_file.gk1k").expect("failed to open file")
)
.expect("error loading from file")
);
let preset = Preset::new(
crate::Effects::VerticalWave,
crate::Color {
red: 0,
green: 0,
blue: 0,
},
true,
2,
9,
2,
None,
);
preset
.save_to_file(&mut File::create("/tmp/test_file.gk1k").expect("Failed to create file"))
.expect("failed to save");
assert_eq!(
preset,
Preset::load_from_file(
&mut File::open("/tmp/test_file.gk1k").expect("failed to open file")
)
.expect("error loading from file")
);
let preset = Preset::new(
crate::Effects::HorizontalWave,
crate::Color {
red: 67,
green: 69,
blue: 255,
},
false,
2,
9,
1,
None,
);
preset
.save_to_file(&mut File::create("/tmp/test_file.gk1k").expect("Failed to create file"))
.expect("failed to save");
assert_eq!(
preset,
Preset::load_from_file(
&mut File::open("/tmp/test_file.gk1k").expect("failed to open file")
)
.expect("error loading from file")
);
let mut keymap: Keymap = Default::default();
keymap.set_all_keys(crate::Color {
red: 255,
green: 9,
blue: 7,
});
keymap.set_key(
crate::KeymapKeys::C,
crate::Color {
red: 111,
green: 222,
blue: 133,
},
);
keymap.set_key(
crate::KeymapKeys::NumPlus,
crate::Color {
red: 112,
green: 223,
blue: 134,
},
);
let preset = Preset::new(
crate::Effects::StaticPerKey,
crate::Color {
red: 0,
green: 0,
blue: 0,
},
false,
16,
1,
0,
Some(keymap.map()),
);
preset
.save_to_file(&mut File::create("/tmp/test_file.gk1k").expect("Failed to create file"))
.expect("failed to save");
assert_eq!(
preset,
Preset::load_from_file(
&mut File::open("/tmp/test_file.gk1k").expect("failed to open file")
)
.expect("error loading from file")
);
#[allow(unused_must_use)]
fs::remove_file("/tmp/test_file.gk1k");
}
}