Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bb63c2689 | ||
|
|
26c8e1d5da | ||
|
|
8f76b4cca5 | ||
|
|
771c95b215 | ||
|
|
85e5bed320 | ||
|
|
58b9216c02 | ||
|
|
ccf67f4a88 | ||
|
|
400e915bce | ||
|
|
cb71613d54 | ||
|
|
1509425ca8 | ||
|
|
d3c233f0e2 | ||
|
|
bbaa0cb96e | ||
|
|
5b25575de2 | ||
|
|
77f7b1abfe | ||
|
|
17a8bbe42f |
No files matched your search
@@ -7,19 +7,18 @@ on:
|
||||
branches: [ "master" ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libudev-dev pkg-config
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: cargo build --verbose --all-features
|
||||
|
||||
+5
-1
@@ -4,12 +4,16 @@ authors = ["vextrixes"]
|
||||
description = "Simple libary to control the CZC gk1000 keyboard"
|
||||
repository = "https://github.com/vextrixes/gk1000-controller"
|
||||
license = "Unlicense"
|
||||
version = "1.0.2"
|
||||
version = "1.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
hidapi = "2.6.6"
|
||||
|
||||
[features]
|
||||
access_hid_wrapper = []
|
||||
preset_file_saving = []
|
||||
fast_controller_experimental = []
|
||||
@@ -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
@@ -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
@@ -1,4 +1,4 @@
|
||||
#[derive(Default)]
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
pub struct Color {
|
||||
pub red: u8,
|
||||
pub green: u8,
|
||||
|
||||
File renamed without changes.
@@ -251,6 +251,7 @@ impl Keymap {
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
/// Returns an array of colors in the placement that the keyboard supports
|
||||
pub fn map(&self) -> [[u8; 3]; 144] {
|
||||
self.map
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ mod color;
|
||||
mod hid_wrapper;
|
||||
mod keymap;
|
||||
mod rgb_controller;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use color::Color;
|
||||
pub use hid_wrapper::HidWrapperError;
|
||||
@@ -11,6 +13,12 @@ pub use rgb_controller::effect_info::{
|
||||
DirectionPolicy, EFFECTS, EffectInfo, HorizontalDirections, VerticalDirections,
|
||||
};
|
||||
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")]
|
||||
pub use hid_wrapper::HidWrapper;
|
||||
@@ -2,8 +2,8 @@ use crate::Color;
|
||||
use crate::Effects;
|
||||
use crate::HidWrapperError;
|
||||
use crate::Keymap;
|
||||
use crate::Preset;
|
||||
use crate::hid_wrapper::HidWrapper;
|
||||
use crate::rgb_controller::effects::Effect;
|
||||
|
||||
pub struct RGBController {
|
||||
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> {
|
||||
self.hid_wrapper.is_device_connected()
|
||||
}
|
||||
@@ -225,7 +229,7 @@ impl RGBController {
|
||||
speed: u8,
|
||||
direction: u8,
|
||||
keymap: Option<[[u8; 3]; 144]>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.hid_wrapper.open_device()?;
|
||||
|
||||
self.prepare_device()?;
|
||||
@@ -264,7 +268,7 @@ impl RGBController {
|
||||
|
||||
self.hid_wrapper.close_device();
|
||||
|
||||
Ok(Effect {
|
||||
Ok(Preset {
|
||||
effect,
|
||||
color,
|
||||
full_color,
|
||||
@@ -280,7 +284,7 @@ impl RGBController {
|
||||
color: Color,
|
||||
brightness: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Static,
|
||||
color,
|
||||
@@ -298,7 +302,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::SingleOff,
|
||||
color,
|
||||
@@ -316,7 +320,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::SingleOn,
|
||||
color,
|
||||
@@ -334,7 +338,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Glittering,
|
||||
color,
|
||||
@@ -352,7 +356,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Rain,
|
||||
color,
|
||||
@@ -368,7 +372,7 @@ impl RGBController {
|
||||
&mut self,
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Colorful,
|
||||
Color::default(),
|
||||
@@ -386,7 +390,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Breath,
|
||||
color,
|
||||
@@ -402,7 +406,7 @@ impl RGBController {
|
||||
&mut self,
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Spectrum,
|
||||
Color::default(),
|
||||
@@ -420,7 +424,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::CentrifugalWave,
|
||||
color,
|
||||
@@ -439,7 +443,7 @@ impl RGBController {
|
||||
speed: u8,
|
||||
direction: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::VerticalWave,
|
||||
color,
|
||||
@@ -458,7 +462,7 @@ impl RGBController {
|
||||
speed: u8,
|
||||
direction: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::HorizontalWave,
|
||||
color,
|
||||
@@ -476,7 +480,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Rotating,
|
||||
color,
|
||||
@@ -494,7 +498,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Explosion,
|
||||
color,
|
||||
@@ -512,7 +516,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Launch,
|
||||
color,
|
||||
@@ -530,7 +534,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Ripples,
|
||||
color,
|
||||
@@ -548,7 +552,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Snake,
|
||||
color,
|
||||
@@ -566,7 +570,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Pulse,
|
||||
color,
|
||||
@@ -584,7 +588,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Tilt,
|
||||
color,
|
||||
@@ -602,7 +606,7 @@ impl RGBController {
|
||||
brightness: u8,
|
||||
speed: u8,
|
||||
full_color: Option<bool>,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::Shuttle,
|
||||
color,
|
||||
@@ -618,7 +622,7 @@ impl RGBController {
|
||||
&mut self,
|
||||
brightness: u8,
|
||||
keymap: Keymap,
|
||||
) -> Result<Effect, HidWrapperError> {
|
||||
) -> Result<Preset, HidWrapperError> {
|
||||
self.set_effect(
|
||||
Effects::StaticPerKey,
|
||||
Color::default(),
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use crate::Effects;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EffectInfo<'a> {
|
||||
name: &'a str,
|
||||
effect: Effects,
|
||||
can_set_color: bool,
|
||||
can_set_speed: bool,
|
||||
can_set_direction: DirectionPolicy,
|
||||
can_set_full_color: bool,
|
||||
requires_keymap: bool,
|
||||
pub name: &'a str,
|
||||
pub effect: Effects,
|
||||
pub can_set_color: bool,
|
||||
pub can_set_speed: bool,
|
||||
pub can_set_direction: DirectionPolicy,
|
||||
pub can_set_full_color: bool,
|
||||
pub requires_keymap: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum DirectionPolicy {
|
||||
None,
|
||||
Horizontal01,
|
||||
|
||||
@@ -4,10 +4,10 @@ use Effects::{
|
||||
VerticalWave,
|
||||
};
|
||||
|
||||
use crate::Color;
|
||||
|
||||
/// Effects of the keyboard
|
||||
#[derive(Clone, Default, PartialEq, Debug)]
|
||||
pub enum Effects {
|
||||
#[default]
|
||||
Static,
|
||||
SingleOn,
|
||||
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 effect_info;
|
||||
pub mod effects;
|
||||
pub mod preset;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#[cfg(feature = "preset_file_saving")]
|
||||
mod preset_saving;
|
||||
|
||||
mod keymap;
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user