Initial Commit

This commit is contained in:
vextrixes committed 2026-05-31 12:20:48 +02:00
commit 1ab3ddf273
13 files changed
+1267

No files matched your search

+133
View File
@@ -0,0 +1,133 @@
pkg/
*.tar
### VisualStudioCode template
.vscode/*
#!.vscode/settings.json
#!.vscode/tasks.json
#!.vscode/launch.json
#!.vscode/extensions.json
#!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### Rust template
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Linux template
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
+7
View File
@@ -0,0 +1,7 @@
[package]
name = "gk1000_controller"
version = "1.0.0"
edition = "2024"
[dependencies]
hidapi = "2.6.5"
+24
View File
@@ -0,0 +1,24 @@
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
View File
@@ -0,0 +1 @@
# gk1000
+7
View File
@@ -0,0 +1,7 @@
extern crate hidapi;
mod constants;
pub mod error;
pub mod wrapper;
pub use error::HidWrapperError;
pub use wrapper::HidWrapper;
+4
View File
@@ -0,0 +1,4 @@
pub const VENDOR_ID: u16 = 0x0c45;
pub const PRODUCT_ID: u16 = 0x8006;
pub const MAX_SEND_REPORT_LEN: usize = 65;
pub const GET_REPORT_REPORT: [u8; MAX_SEND_REPORT_LEN] = [0u8; MAX_SEND_REPORT_LEN];
+40
View File
@@ -0,0 +1,40 @@
use hidapi::HidError;
use std::error::Error;
use std::fmt::Display;
#[derive(Debug)]
pub enum HidWrapperError {
#[allow(dead_code)]
HidApiError(HidError),
NoHidDeviceError,
}
impl Display for HidWrapperError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HidWrapperError::HidApiError(err) => {
write!(f, "Hid Api Error: {err}")
}
HidWrapperError::NoHidDeviceError => {
write!(f, "No Hid Device Found")
}
}
}
}
impl Error for HidWrapperError {}
impl From<HidError> for HidWrapperError {
fn from(error: HidError) -> Self {
match error {
HidError::HidApiError { ref message } => {
if message == "No HID devices with requested VID/PID found in the system." {
HidWrapperError::NoHidDeviceError
} else {
HidWrapperError::HidApiError(error)
}
}
_ => HidWrapperError::HidApiError(error),
}
}
}
+108
View File
@@ -0,0 +1,108 @@
use crate::hid_wrapper::constants::{
GET_REPORT_REPORT, MAX_SEND_REPORT_LEN, PRODUCT_ID, VENDOR_ID,
};
use crate::hid_wrapper::error::HidWrapperError;
use hidapi::{HidApi, HidDevice};
pub struct HidWrapper {
hid_api: HidApi,
hid_device: Option<HidDevice>,
}
impl Default for HidWrapper {
/// Makes a new instance of HidWrapper (No shit Sherlock)
///
/// # Panics
/// If ```HidWrapperError::HidApiError``` is returned from new()
fn default() -> Self {
match Self::new() {
Ok(hid_wrapper) => hid_wrapper,
Err(err) => panic!("hid wrapper Default() failed to create new HidWrapper: {err}"),
}
}
}
impl HidWrapper {
/// Makes a new instance of HidWrapper (No shit Sherlock)
///
/// # Errors
/// ```HidWrapperError::HidApiError```
pub fn new() -> Result<HidWrapper, HidWrapperError> {
Ok(HidWrapper {
hid_api: match HidApi::new() {
Ok(hidapi) => hidapi,
Err(err) => return Err(HidWrapperError::from(err)),
},
hid_device: None,
})
}
pub fn is_device_connected(&mut self) -> Result<bool, HidWrapperError> {
match self.hid_api.reset_devices() {
Ok(_) => {}
Err(err) => return Err(HidWrapperError::from(err)),
};
match self.hid_api.add_devices(VENDOR_ID, PRODUCT_ID) {
Ok(_) => {}
Err(err) => return Err(HidWrapperError::from(err)),
};
Ok(self.hid_api.device_list().count() >= 1)
}
/// Opens a device
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
pub fn open_device(&mut self) -> Result<(), HidWrapperError> {
self.hid_device = match self.hid_api.open(VENDOR_ID, PRODUCT_ID) {
Ok(hid_device) => Some(hid_device),
Err(err) => return Err(HidWrapperError::from(err)),
};
Ok(())
}
/// Drops the device
///
pub fn close_device(&mut self) {
self.hid_device = None;
}
/// Sends a set report (65 bytes len)
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
pub fn send_report(&mut self, data: &[u8]) -> Result<(), HidWrapperError> {
match &self.hid_device {
Some(hid_device) => {
let mut out = [0; MAX_SEND_REPORT_LEN];
out[1..1 + data.len()].copy_from_slice(data);
match hid_device.send_feature_report(&out) {
Ok(_) => Ok(()),
Err(err) => Err(HidWrapperError::from(err)),
}
}
None => Err(HidWrapperError::NoHidDeviceError),
}
}
/// Sends a get report (64 bytes len)
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
pub fn get_report(&mut self) -> Result<usize, HidWrapperError> {
match &self.hid_device {
Some(hid_device) => {
let mut request = GET_REPORT_REPORT;
request[1] = 64;
match hid_device.get_feature_report(&mut request) {
Ok(usize) => Ok(usize),
Err(err) => Err(HidWrapperError::from(err)),
}
}
None => Err(HidWrapperError::NoHidDeviceError),
}
}
}
+6
View File
@@ -0,0 +1,6 @@
mod hid_wrapper;
mod rgb_controller;
pub use hid_wrapper::HidWrapperError;
pub use rgb_controller::controller::RGBController;
pub use rgb_controller::effects::Effects;
+3
View File
@@ -0,0 +1,3 @@
pub mod controller;
pub mod effect_info;
pub mod effects;
+695
View File
@@ -0,0 +1,695 @@
use crate::Effects;
use crate::HidWrapperError;
use crate::hid_wrapper::HidWrapper;
pub struct RGBController {
hid_wrapper: HidWrapper,
}
impl Default for RGBController {
/// Makes a new instance of HidWrapper (No shit Sherlock)
///
/// # Panics
/// if ```HidWrapperError::HidApiError``` is returned from new()
fn default() -> Self {
match Self::new() {
Ok(hid_wrapper) => hid_wrapper,
Err(err) => panic!("RGBController Default() failed to create new RGBController: {err}"),
}
}
}
impl RGBController {
/// Makes a new instance of HidWrapper (No shit Sherlock)
///
/// # Errors
/// ```HidWrapperError::HidApiError```
pub fn new() -> Result<RGBController, HidWrapperError> {
Ok(RGBController {
hid_wrapper: HidWrapper::new()?,
})
}
pub fn is_device_connected(&mut self) -> Result<bool, HidWrapperError> {
self.hid_wrapper.is_device_connected()
}
/// Starts the RGB conversation
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
fn prepare_device(&mut self) -> Result<(), HidWrapperError> {
self.hid_wrapper.send_report(&[0x04, 0xab])?;
self.hid_wrapper.get_report()?;
self.hid_wrapper.send_report(&[
0x26, 0x36, 0xf2, 0x27, 0xb7, 0x2a, 0x56, 0x77, 0x98, 0x59, 0x9c, 0x6d, 0x9f, 0xc6,
0xb, 0x44, 0xec, 0xa8, 0x47, 0x21, 0xe2, 0x65, 0x76, 0xa3, 0x70, 0xbe, 0xa2, 0xbb,
0x95, 0x47, 0x5e, 0xbe, 0x23, 0x1b, 0xad, 0xb8, 0xc3, 0xcd, 0x1f, 0x4, 0x39, 0xa2,
0x3d, 0x19, 0xe9, 0xfd, 0x27, 0xa7, 0x9a, 0x1a, 0x7c, 0xe3, 0x7b, 0xff, 0x7a, 0x1e,
0x63, 0xb0, 0x67, 0x9d, 0x5d, 0xa3, 0x2f, 0xa5,
])?;
self.hid_wrapper.send_report(&[0x04, 0x02])?;
self.hid_wrapper.get_report()?;
self.hid_wrapper
.send_report(&[0x04, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12])?;
self.hid_wrapper.get_report()?;
Ok(())
}
/// Some middle bytes IDK what to name ts
/// Is a required part of the RGB conversation
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
fn mid(&mut self) -> Result<(), HidWrapperError> {
//TODO: Rename func and possibly change packets depending on mode
self.hid_wrapper.send_report(&[
0x1, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x2, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x3, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x4, 0x5a, 0xff, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
])?;
self.hid_wrapper.send_report(&[
0x5, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x6, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x7, 0x5a, 0xff, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x8, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
])?;
self.hid_wrapper.send_report(&[
0x9, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0xa, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0xb, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0xc, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
])?;
self.hid_wrapper.send_report(&[
0xd, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0xe, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0xf, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x10, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
])?;
self.hid_wrapper.send_report(&[
0x11, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x12, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x13, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x1, 0x10, 0xc, 0x0, 0x0, 0x0, 0xaa, 0x55,
0x80, 0xff, 0xff, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x55,
])?;
Ok(())
}
/// Sets colors for each key (only if StaticPerKey effect is set) base on a 2d array
/// Is a required part of the RGB conversation
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
fn custom_keys(&mut self, keymap: &[[u8; 3]; 144]) -> Result<(), HidWrapperError> {
for i in 0..9 {
//Magic num 9 == len of keymap / 16
self.hid_wrapper.send_report(&[
0x80,
keymap[i * 16][0],
keymap[i * 16][1],
keymap[i * 16][2],
0x80,
keymap[i * 16 + 1][0],
keymap[i * 16 + 1][1],
keymap[i * 16 + 1][2],
0x80,
keymap[i * 16 + 2][0],
keymap[i * 16 + 2][1],
keymap[i * 16 + 2][2],
0x80,
keymap[i * 16 + 3][0],
keymap[i * 16 + 3][1],
keymap[i * 16 + 3][2],
0x80,
keymap[i * 16 + 4][0],
keymap[i * 16 + 4][1],
keymap[i * 16 + 4][2],
0x80,
keymap[i * 16 + 5][0],
keymap[i * 16 + 5][1],
keymap[i * 16 + 5][2],
0x80,
keymap[i * 16 + 6][0],
keymap[i * 16 + 6][1],
keymap[i * 16 + 6][2],
0x80,
keymap[i * 16 + 7][0],
keymap[i * 16 + 7][1],
keymap[i * 16 + 7][2],
0x80,
keymap[i * 16 + 8][0],
keymap[i * 16 + 8][1],
keymap[i * 16 + 8][2],
0x80,
keymap[i * 16 + 9][0],
keymap[i * 16 + 9][1],
keymap[i * 16 + 9][2],
0x80,
keymap[i * 16 + 10][0],
keymap[i * 16 + 10][1],
keymap[i * 16 + 10][2],
0x80,
keymap[i * 16 + 11][0],
keymap[i * 16 + 11][1],
keymap[i * 16 + 11][2],
0x80,
keymap[i * 16 + 12][0],
keymap[i * 16 + 12][1],
keymap[i * 16 + 12][2],
0x80,
keymap[i * 16 + 13][0],
keymap[i * 16 + 13][1],
keymap[i * 16 + 13][2],
0x80,
keymap[i * 16 + 14][0],
keymap[i * 16 + 14][1],
keymap[i * 16 + 14][2],
0x80,
keymap[i * 16 + 15][0],
keymap[i * 16 + 15][1],
keymap[i * 16 + 15][2],
])?;
}
Ok(())
}
/// Applies an effect to the device
/// Is a required part of the RGB conversation
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
fn apply_to_device(&mut self) -> Result<(), HidWrapperError> {
self.hid_wrapper.send_report(&[0x04, 0x02])?;
self.hid_wrapper.get_report()?;
self.hid_wrapper.send_report(&[0x04, 0xf0])?;
Ok(())
}
/// Sets an effect
///
/// # Errors
/// ```HidWrapperError::HidApiError```
/// ```HidWrapperError::NoHidDeviceError```
#[allow(clippy::too_many_arguments)]
pub fn set_effect(
&mut self,
effect: Effects,
red: u8,
green: u8,
blue: u8,
full_color: bool,
brightness: u8,
speed: u8,
direction: u8,
keymap: Option<[[u8; 3]; 144]>,
) -> Result<(), HidWrapperError> {
self.hid_wrapper.open_device()?;
self.prepare_device()?;
self.mid()?;
self.hid_wrapper.send_report(&[])?;
self.hid_wrapper.send_report(&[])?;
self.hid_wrapper.send_report(&[])?;
match keymap {
Some(map) => self.custom_keys(&map)?,
None => self.custom_keys(&[[0, 255, 0]; 144])?,
}
self.hid_wrapper.send_report(&[
effect.to_u8(),
red,
green,
blue,
0,
0,
0,
0,
u8::from(full_color),
brightness.clamp(0, 16),
speed.clamp(1, 16),
direction.clamp(0, 3),
0,
0,
0xaa,
0x55,
])?;
self.apply_to_device()?;
self.hid_wrapper.close_device();
Ok(())
}
pub fn set_static_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Static,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
1,
0,
None,
)
}
pub fn set_single_off_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::SingleOff,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_single_on_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::SingleOn,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_glittering_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Glittering,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_rain_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Rain,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_colorful_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Colorful,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_breath_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Breath,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_spectrum_effect(
&mut self,
brightness: u8,
speed: u8,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Spectrum,
0,
0,
0,
false,
brightness,
speed,
0,
None,
)
}
pub fn set_centrifugal_wave_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::CentrifugalWave,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
#[allow(clippy::too_many_arguments)]
pub fn set_vertical_wave_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
direction: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::VerticalWave,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
direction.clamp(2, 3),
None,
)
}
#[allow(clippy::too_many_arguments)]
pub fn set_horizontal_wave_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
direction: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::HorizontalWave,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
direction.clamp(0, 1),
None,
)
}
pub fn set_rotating_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Rotating,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_explosion_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Explosion,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_launch_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Launch,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_ripples_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Ripples,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_snake_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Snake,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_pulse_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Pulse,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_tilt_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Tilt,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_shuttle_effect(
&mut self,
red: u8,
green: u8,
blue: u8,
brightness: u8,
speed: u8,
full_color: Option<bool>,
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::Shuttle,
red,
green,
blue,
full_color.unwrap_or(false),
brightness,
speed,
0,
None,
)
}
pub fn set_static_per_key_effect(
&mut self,
brightness: u8,
keymap: [[u8; 3]; 144],
) -> Result<(), HidWrapperError> {
self.set_effect(
Effects::StaticPerKey,
0,
0,
0,
false,
brightness,
1,
0,
Some(keymap),
)
}
}
+182
View File
@@ -0,0 +1,182 @@
use crate::Effects;
#[allow(dead_code)]
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,
}
#[allow(dead_code)]
pub enum DirectionPolicy {
None,
Horizontal01,
Vertical23,
}
#[allow(dead_code)]
pub const EFFECTS: [EffectInfo; 20] = [
EffectInfo {
name: "Static",
effect: Effects::Static,
can_set_color: true,
can_set_speed: false,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "SingleOn",
effect: Effects::SingleOn,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "SingleOff",
effect: Effects::SingleOff,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Glittering",
effect: Effects::Glittering,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Rain",
effect: Effects::Rain,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Colorful",
effect: Effects::Colorful,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Breath",
effect: Effects::Breath,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Spectrum",
effect: Effects::Spectrum,
can_set_color: false,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: false,
},
EffectInfo {
name: "CentrifugalWave",
effect: Effects::CentrifugalWave,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "VerticalWave",
effect: Effects::VerticalWave,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::Vertical23,
can_set_full_color: true,
},
EffectInfo {
name: "HorizontalWave",
effect: Effects::HorizontalWave,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::Horizontal01,
can_set_full_color: true,
},
EffectInfo {
name: "Rotating",
effect: Effects::Rotating,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Explosion",
effect: Effects::Explosion,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Launch",
effect: Effects::Launch,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Ripples",
effect: Effects::Ripples,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Snake",
effect: Effects::Snake,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Pulse",
effect: Effects::Pulse,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Tilt",
effect: Effects::Tilt,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "Shuttle",
effect: Effects::Shuttle,
can_set_color: true,
can_set_speed: true,
can_set_direction: DirectionPolicy::None,
can_set_full_color: true,
},
EffectInfo {
name: "StaticPerKey",
effect: Effects::StaticPerKey,
can_set_color: false,
can_set_speed: false,
can_set_direction: DirectionPolicy::None,
can_set_full_color: false,
},
];
+57
View File
@@ -0,0 +1,57 @@
use Effects::{
Breath, CentrifugalWave, Colorful, Explosion, Glittering, HorizontalWave, Launch, Pulse, Rain,
Ripples, Rotating, Shuttle, SingleOff, SingleOn, Snake, Spectrum, Static, StaticPerKey, Tilt,
VerticalWave,
};
/// Effects of the keyboard
pub enum Effects {
Static,
SingleOn,
SingleOff,
Glittering,
Rain,
Colorful,
Breath,
Spectrum,
CentrifugalWave,
VerticalWave,
HorizontalWave,
Rotating,
Explosion,
Launch,
Ripples,
Snake,
Pulse,
Tilt,
Shuttle,
StaticPerKey,
}
impl Effects {
/// Returns an u8 of the effect used for communication with the device
pub fn to_u8(&self) -> u8 {
match self {
Static => 1,
SingleOn => 2,
SingleOff => 3,
Glittering => 4,
Rain => 5,
Colorful => 6,
Breath => 7,
Spectrum => 8,
CentrifugalWave => 9,
VerticalWave => 10,
HorizontalWave => 11,
Rotating => 12,
Explosion => 13,
Launch => 14,
Ripples => 15,
Snake => 16,
Pulse => 17,
Tilt => 18,
Shuttle => 19,
StaticPerKey => 20,
}
}
}