#![deny(
warnings,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unstable_features,
unused_import_braces,
unused_qualifications,
missing_docs,
unused_extern_crates,
unused_qualifications,
unused_results
)]
use std::error::Error;
use std::fmt::{self, Display};
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum WootingError {
Disconnected,
InvalidBufferSize,
}
impl Display for WootingError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
WootingError::Disconnected => write!(fmt, "Wooting keyboard is not connected"),
WootingError::InvalidBufferSize => {
write!(fmt, "Requested analog value of too many keys")
}
}
}
}
impl Error for WootingError {}
pub trait IntoMatrixRowColumn {
fn into_matrix_row_and_column(&self) -> (u8, u8);
}
pub trait FromScanIndex: Sized {
fn from_scan_index(index: u8) -> Option<Self>;
}
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum Key {
Escape,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
PrintScreen,
Pause,
ScrollLock,
A1,
A2,
A3,
Mode,
Tilde,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Zero,
Dash,
Equals,
Backspace,
Insert,
Home,
PageUp,
NumLock,
NumDivide,
NumMultiply,
NumSubtract,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LeftBracket,
RightBracket,
Backslash,
Delete,
End,
PageDown,
NumSeven,
NumEight,
NumNine,
NumAddition,
CapsLock,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Apostrophe,
ISO1,
Return,
NumFour,
NumFive,
NumSix,
LeftShift,
ISO2,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
ForwardSlash,
RightShift,
UpArrow,
NumOne,
NumTwo,
NumThree,
NumReturn,
LeftControl,
LeftMod,
LeftAlt,
Space,
RightAlt,
RightMod,
Fn,
RightControl,
LeftArrow,
DownArrow,
RightArrow,
NumZero,
NumDelete,
}
impl Display for Key {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
use Key::*;
write!(
fmt,
"{}",
match self {
Escape => "Esc",
F1 => "F1",
F2 => "F2",
F3 => "F3",
F4 => "F4",
F5 => "F5",
F6 => "F6",
F7 => "F7",
F8 => "F8",
F9 => "F9",
F10 => "F10",
F11 => "F11",
F12 => "F12",
PrintScreen => "Print Screen",
Pause => "Pause",
ScrollLock => "Scroll Lock",
A1 => "A1",
A2 => "A2",
A3 => "A3",
Mode => "Mode",
Tilde => "~",
One => "1",
Two => "2",
Three => "3",
Four => "4",
Five => "5",
Six => "6",
Seven => "7",
Eight => "8",
Nine => "9",
Zero => "0",
Dash => "-",
Equals => "=",
Backspace => "Backspace",
Insert => "Insert",
Home => "Home",
PageUp => "Page Up",
NumLock => "Num Lock",
NumDivide => "\\",
NumMultiply => "*",
NumSubtract => "-",
Tab => "Tab",
Q => "Q",
W => "W",
E => "E",
R => "R",
T => "T",
Y => "Y",
U => "U",
I => "I",
O => "O",
P => "P",
LeftBracket => "[",
RightBracket => "]",
Backslash => "\\",
Delete => "Delete",
End => "End",
PageDown => "Page Down",
NumSeven => "7",
NumEight => "8",
NumNine => "9",
NumAddition => "+",
CapsLock => "Caps Lock",
A => "A",
S => "S",
D => "D",
F => "F",
G => "G",
H => "H",
J => "J",
K => "K",
L => "L",
SemiColon => ";",
Apostrophe => "'",
ISO1 => "ISO",
Return => "Return",
NumFour => "4",
NumFive => "5",
NumSix => "6",
LeftShift => "Left Shift",
ISO2 => "ISO",
Z => "Z",
X => "X",
C => "C",
V => "V",
B => "B",
N => "N",
M => "M",
Comma => "Comma",
Period => "Period",
ForwardSlash => "/",
RightShift => "Right Shift",
UpArrow => "Up Arrow",
NumOne => "1",
NumTwo => "2",
NumThree => "3",
NumReturn => "Return",
LeftControl => "Left Control",
LeftMod => "Left Mod",
LeftAlt => "Left Alt",
Space => "Space",
RightAlt => "Right Alt",
RightMod => "Right Mod",
Fn => "Fn",
RightControl => "Right Control",
LeftArrow => "Left Arrow",
DownArrow => "Down Arrow",
RightArrow => "Right Arrow",
NumZero => "0",
NumDelete => "Delete",
}
)
}
}
impl FromScanIndex for Key {
fn from_scan_index(index: u8) -> Option<Self> {
use Key::*;
Some(match index {
0 => Escape,
1 => F1,
2 => F2,
3 => F3,
4 => F4,
5 => F5,
6 => F6,
7 => F7,
8 => F8,
9 => F9,
10 => F10,
11 => F11,
12 => F12,
13 => PrintScreen,
14 => Pause,
15 => ScrollLock,
16 => Tilde,
17 => One,
18 => Two,
19 => Three,
20 => Four,
21 => Five,
22 => Six,
23 => Seven,
24 => Eight,
25 => Nine,
26 => Zero,
27 => Dash,
28 => Equals,
29 => Backspace,
30 => Insert,
31 => Home,
32 => Tab,
33 => Q,
34 => W,
35 => E,
36 => R,
37 => T,
38 => Y,
39 => U,
40 => I,
41 => O,
42 => P,
43 => LeftBracket,
44 => RightBracket,
45 => ISO1,
46 => Delete,
47 => End,
48 => CapsLock,
49 => A,
50 => S,
51 => D,
52 => F,
53 => G,
54 => H,
55 => I,
56 => J,
57 => K,
58 => L,
59 => SemiColon,
60 => Return,
61 => PageUp,
62 => PageDown,
63 => UpArrow,
64 => LeftShift,
65 => Z,
66 => X,
67 => C,
68 => V,
69 => B,
70 => N,
71 => M,
72 => Comma,
73 => Period,
74 => ForwardSlash,
75 => RightShift,
76 => LeftArrow,
77 => DownArrow,
78 => RightArrow,
79 => RightControl,
80 => LeftControl,
81 => LeftMod,
82 => LeftAlt,
83 => Space,
84 => RightAlt,
85 => RightMod,
86 => Fn,
87 => ISO2,
90 => NumOne,
91 => NumTwo,
92 => NumThree,
93 => NumReturn,
94 => NumDelete,
95 => NumZero,
96 => NumSix,
97 => NumFive,
98 => NumFour,
99 => NumAddition,
100 => NumNine,
101 => NumEight,
102 => NumSeven,
103 => NumSubtract,
104 => NumMultiply,
105 => NumDivide,
106 => NumLock,
107 => A1,
108 => A2,
109 => A3,
110 => Mode,
_ => return None,
})
}
}
impl IntoMatrixRowColumn for Key {
fn into_matrix_row_and_column(&self) -> (u8, u8) {
use Key::*;
match self {
Escape => (0, 0),
F1 => (0, 2),
F2 => (0, 3),
F3 => (0, 4),
F4 => (0, 5),
F5 => (0, 6),
F6 => (0, 7),
F7 => (0, 8),
F8 => (0, 9),
F9 => (0, 10),
F10 => (0, 11),
F11 => (0, 12),
F12 => (0, 13),
PrintScreen => (0, 14),
Pause => (0, 15),
ScrollLock => (0, 16),
A1 => (0, 17),
A2 => (0, 18),
A3 => (0, 19),
Mode => (0, 20),
Tilde => (1, 0),
One => (1, 1),
Two => (1, 2),
Three => (1, 3),
Four => (1, 4),
Five => (1, 5),
Six => (1, 6),
Seven => (1, 7),
Eight => (1, 8),
Nine => (1, 9),
Zero => (1, 10),
Dash => (1, 11),
Equals => (1, 12),
Backspace => (1, 13),
Insert => (1, 14),
Home => (1, 15),
PageUp => (1, 16),
NumLock => (1, 17),
NumDivide => (1, 18),
NumMultiply => (1, 19),
NumSubtract => (1, 20),
Tab => (2, 0),
Q => (2, 1),
W => (2, 2),
E => (2, 3),
R => (2, 4),
T => (2, 5),
Y => (2, 6),
U => (2, 7),
I => (2, 8),
O => (2, 9),
P => (2, 10),
LeftBracket => (2, 11),
RightBracket => (2, 12),
Backslash => (2, 13),
Delete => (2, 14),
End => (2, 15),
PageDown => (2, 16),
NumSeven => (2, 17),
NumEight => (2, 18),
NumNine => (2, 19),
NumAddition => (2, 20),
CapsLock => (3, 0),
A => (3, 1),
S => (3, 2),
D => (3, 3),
F => (3, 4),
G => (3, 5),
H => (3, 6),
J => (3, 7),
K => (3, 8),
L => (3, 9),
SemiColon => (3, 10),
Apostrophe => (3, 11),
ISO1 => (3, 12),
Return => (3, 13),
NumFour => (3, 17),
NumFive => (3, 18),
NumSix => (3, 19),
LeftShift => (4, 0),
ISO2 => (4, 1),
Z => (4, 2),
X => (4, 3),
C => (4, 4),
V => (4, 5),
B => (4, 6),
N => (4, 7),
M => (4, 8),
Comma => (4, 9),
Period => (4, 10),
ForwardSlash => (4, 11),
RightShift => (4, 13),
UpArrow => (4, 15),
NumOne => (4, 17),
NumTwo => (4, 18),
NumThree => (4, 19),
NumReturn => (4, 20),
LeftControl => (5, 0),
LeftMod => (5, 1),
LeftAlt => (5, 2),
Space => (5, 6),
RightAlt => (5, 10),
RightMod => (5, 11),
Fn => (5, 12),
RightControl => (5, 13),
LeftArrow => (5, 14),
DownArrow => (5, 15),
RightArrow => (5, 16),
NumZero => (5, 18),
NumDelete => (5, 19),
}
}
}
#[cfg(feature = "analog")]
pub mod analog {
use super::{FromScanIndex, IntoMatrixRowColumn, WootingError};
use std::sync::Mutex;
use lazy_static::lazy_static;
use wooting_analog_sdk_sys;
lazy_static! {
static ref CALLBACK: Mutex<Option<Box<dyn Fn() + Send>>> = Default::default();
}
pub fn is_wooting_keyboard_connected() -> bool {
unsafe { wooting_analog_sdk_sys::wooting_kbd_connected() }
}
extern "C" fn set_disconnected_callback_handler() {
if let Some(ref mut callback) = *CALLBACK.lock().unwrap() {
callback();
} else {
panic!("Callback static has not been set");
}
}
pub fn set_disconnected_callback<F: 'static + Fn() + Send>(callback: F) {
*CALLBACK.lock().unwrap() = Some(Box::new(callback));
unsafe {
wooting_analog_sdk_sys::wooting_set_disconnected_cb(Some(
set_disconnected_callback_handler,
));
}
}
pub fn read_analog_key<K: IntoMatrixRowColumn>(key: K) -> Result<u8, WootingError> {
let (row, column) = key.into_matrix_row_and_column();
let ret = unsafe { wooting_analog_sdk_sys::wooting_read_analog(row, column) };
if ret == 0 && !is_wooting_keyboard_connected() {
Err(WootingError::Disconnected)
} else {
Ok(ret)
}
}
pub fn read_analog_keys<K: FromScanIndex>(n: u8) -> Result<Vec<(K, u8)>, WootingError> {
if n == 0 || n > 16 {
return Err(WootingError::InvalidBufferSize);
}
let buffer_length = n as usize * 2;
let mut buffer: Vec<u8> = vec![0; buffer_length];
let ret: i32 = unsafe {
wooting_analog_sdk_sys::wooting_read_full_buffer(
buffer.as_mut_ptr(),
buffer_length as u32,
)
};
if ret == -1 {
Err(WootingError::Disconnected)
} else if ret < -1 {
panic!("Invalid return code from Wooting Analog SDK");
} else {
Ok(buffer
.chunks(2)
.take(ret as usize)
.filter_map(|chunk| match chunk {
&[scan_index, analog_value] => {
K::from_scan_index(scan_index).map(|key| (key, analog_value))
}
_ => unreachable!(),
})
.collect())
}
}
}
#[cfg(feature = "rgb")]
pub mod rgb {
use super::IntoMatrixRowColumn;
use std::sync::Mutex;
use lazy_static::lazy_static;
use wooting_rgb_sdk_sys;
const COLUMNS: usize = 21;
const ROWS: usize = 6;
const COMPONENTS: usize = 3;
lazy_static! {
static ref CALLBACK: Mutex<Option<Box<dyn Fn() + Send>>> = Default::default();
}
pub fn is_wooting_keyboard_connected() -> bool {
unsafe { wooting_rgb_sdk_sys::wooting_rgb_kbd_connected() }
}
extern "C" fn set_disconnected_callback_handler() {
if let Some(ref mut callback) = *CALLBACK.lock().unwrap() {
callback();
} else {
panic!("Callback static has not been set");
}
}
pub fn set_disconnected_callback<F: 'static + Fn() + Send>(callback: F) {
*CALLBACK.lock().unwrap() = Some(Box::new(callback));
unsafe {
wooting_rgb_sdk_sys::wooting_rgb_set_disconnected_cb(Some(
set_disconnected_callback_handler,
));
}
}
#[derive(Clone, Debug, Default)]
pub struct RgbKeyboard;
impl RgbKeyboard {
pub fn direct_set_key<K: IntoMatrixRowColumn>(
&mut self,
key: K,
red: u8,
green: u8,
blue: u8,
) -> bool {
let (row, column) = key.into_matrix_row_and_column();
unsafe {
wooting_rgb_sdk_sys::wooting_rgb_direct_set_key(row, column, red, green, blue)
}
}
pub fn direct_reset_key<K: IntoMatrixRowColumn>(&mut self, key: K) -> bool {
let (row, column) = key.into_matrix_row_and_column();
unsafe { wooting_rgb_sdk_sys::wooting_rgb_direct_reset_key(row, column) }
}
pub fn array_update(&mut self) -> bool {
unsafe { wooting_rgb_sdk_sys::wooting_rgb_array_update_keyboard() }
}
pub fn array_auto_update(&mut self, auto_update: bool) {
unsafe { wooting_rgb_sdk_sys::wooting_rgb_array_auto_update(auto_update) }
}
pub fn array_set_single<K: IntoMatrixRowColumn>(
&mut self,
key: K,
red: u8,
green: u8,
blue: u8,
) -> bool {
let (row, column) = key.into_matrix_row_and_column();
unsafe {
wooting_rgb_sdk_sys::wooting_rgb_array_set_single(row, column, red, green, blue)
}
}
pub fn array_set_full<K: IntoMatrixRowColumn>(
&mut self,
array: &[(K, (u8, u8, u8))],
) -> bool {
let mut flattened: [u8; COMPONENTS * COLUMNS * ROWS] = [0; COMPONENTS * COLUMNS * ROWS];
for (key, (red, green, blue)) in array {
let (row, column) = key.into_matrix_row_and_column();
let index: usize =
(row as usize) * (COLUMNS * COMPONENTS) + (column as usize) * COMPONENTS;
flattened[index] = *red;
flattened[index + 1] = *green;
flattened[index + 2] = *blue;
}
unsafe { wooting_rgb_sdk_sys::wooting_rgb_array_set_full(flattened.as_ptr()) }
}
pub fn reset_all(&mut self) -> bool {
unsafe { wooting_rgb_sdk_sys::wooting_rgb_reset() }
}
}
impl Drop for RgbKeyboard {
fn drop(&mut self) {
let _ = self.reset_all();
self.array_auto_update(false);
}
}
}