1#[macro_use]
2extern crate enum_primitive_derive;
3extern crate ffi_support;
4extern crate num_traits;
5
6use ffi_support::FfiStr;
7pub use num_traits::{FromPrimitive, ToPrimitive};
8#[cfg(feature = "serdes")]
9use serde::{Deserialize, Serialize};
10use std::ffi::{CStr, CString};
11use std::ops::Deref;
12use std::os::raw::{c_char, c_int};
13use thiserror::Error;
14
15#[cfg(target_os = "macos")]
16pub const DEFAULT_PLUGIN_DIR: &str = "/usr/local/share/WootingAnalogPlugins";
17#[cfg(target_os = "linux")]
18pub const DEFAULT_PLUGIN_DIR: &str = "/usr/local/share/WootingAnalogPlugins";
19#[cfg(target_os = "windows")]
20pub const DEFAULT_PLUGIN_DIR: &str = "C:\\Program Files\\WootingAnalogPlugins";
21
22#[cfg_attr(feature = "serdes", derive(Serialize, Deserialize))]
26#[derive(Clone, Debug)]
27pub struct DeviceInfo {
28 pub vendor_id: u16,
30 pub product_id: u16,
32 pub manufacturer_name: String,
34 pub device_name: String,
36 pub device_id: DeviceID,
38 pub device_type: DeviceType,
40}
41
42#[repr(C)]
47pub struct DeviceInfo_FFI {
48 pub vendor_id: u16,
50 pub product_id: u16,
52 pub manufacturer_name: *mut c_char,
54 pub device_name: *mut c_char,
56 pub device_id: DeviceID,
58 pub device_type: DeviceType,
60}
61
62impl From<DeviceInfo> for DeviceInfo_FFI {
63 fn from(device: DeviceInfo) -> Self {
64 DeviceInfo_FFI {
65 vendor_id: device.vendor_id,
66 product_id: device.product_id,
67 manufacturer_name: CString::new(device.manufacturer_name).unwrap().into_raw(),
68 device_name: CString::new(device.device_name).unwrap().into_raw(),
69 device_id: device.device_id,
70 device_type: device.device_type,
71 }
72 }
73}
74
75impl Drop for DeviceInfo_FFI {
76 fn drop(&mut self) {
77 unsafe {
79 let _c_string = CString::from_raw(self.manufacturer_name);
80 let _c_string = CString::from_raw(self.device_name);
81 }
82 }
83}
84
85impl DeviceInfo_FFI {
86 pub fn into_device_info(&self) -> DeviceInfo {
87 DeviceInfo {
88 vendor_id: self.vendor_id.clone(),
89 product_id: self.product_id.clone(),
90 manufacturer_name: unsafe {
93 CStr::from_ptr(self.manufacturer_name)
94 .to_str()
95 .unwrap()
96 .to_owned()
97 },
98 device_name: unsafe {
99 CStr::from_ptr(self.device_name)
100 .to_str()
101 .unwrap()
102 .to_owned()
103 },
104 device_id: self.device_id.clone(),
105 device_type: self.device_type.clone(),
106 }
107 }
108}
109
110impl DeviceInfo {
111 pub fn new_with_id(
130 vendor_id: u16,
131 product_id: u16,
132 manufacturer_name: String,
133 device_name: String,
134 device_id: DeviceID,
135 device_type: DeviceType,
136 ) -> Self {
137 DeviceInfo {
138 vendor_id,
139 product_id,
140 manufacturer_name,
141 device_name,
142 device_id,
143 device_type,
144 }
145 }
146}
147
148#[no_mangle]
153pub extern "C" fn new_device_info(
154 vendor_id: u16,
155 product_id: u16,
156 manufacturer_name: *mut c_char,
157 device_name: *mut c_char,
158 device_id: DeviceID,
159 device_type: DeviceType,
160) -> *mut DeviceInfo {
161 Box::into_raw(Box::new(DeviceInfo::new_with_id(
162 vendor_id,
163 product_id,
164 unsafe {
165 CStr::from_ptr(manufacturer_name)
166 .to_string_lossy()
167 .into_owned()
168 },
169 unsafe { CStr::from_ptr(device_name).to_string_lossy().into_owned() },
170 device_id,
171 device_type,
172 )))
173}
174
175#[no_mangle]
177pub unsafe extern "C" fn drop_device_info(device: *mut DeviceInfo) {
178 Box::from_raw(device);
179}
180
181#[cfg_attr(feature = "serdes", derive(Serialize, Deserialize))]
182#[derive(Debug, PartialEq, Clone, Primitive)]
183#[repr(C)]
184pub enum KeycodeType {
185 HID = 0,
187 ScanCode1 = 1,
189 VirtualKey = 2,
191 VirtualKeyTranslate = 3,
193}
194
195pub type DeviceID = u64;
196
197#[cfg_attr(feature = "serdes", derive(Serialize, Deserialize))]
198#[derive(Debug, PartialEq, Clone, Primitive)]
199#[repr(C)]
200pub enum DeviceType {
201 Keyboard = 1,
203 Keypad = 2,
205 Other = 3,
207}
208
209#[cfg_attr(feature = "serdes", derive(Serialize, Deserialize))]
210#[derive(Debug, PartialEq, Clone, Primitive)]
211#[repr(C)]
212pub enum DeviceEventType {
213 Connected = 1,
215 Disconnected = 2,
217}
218
219#[cfg_attr(feature = "serdes", derive(Serialize, Deserialize))]
220#[derive(Debug, PartialEq, Clone, Primitive, Error)]
221#[repr(C)]
222pub enum WootingAnalogResult {
223 #[error("All OK")]
224 Ok = 1,
225 #[error("SDK has not been initialized")]
227 UnInitialized = -2000isize,
228 #[error("No Devices are connected")]
230 NoDevices = -1999isize,
231 #[error("Device has been disconnected")]
233 DeviceDisconnected = -1998isize,
234 #[error("Generic Failure")]
236 Failure = -1997isize,
237 #[error("A given parameter was invalid")]
239 InvalidArgument = -1996isize,
240 #[error("No Plugins were found")]
242 NoPlugins = -1995isize,
243 #[error("The specified function was not found in the library")]
245 FunctionNotFound = -1994isize,
246 #[error("No Keycode mapping to HID was found for the given Keycode")]
248 NoMapping = -1993isize,
249 #[error("Unavailable on this platform")]
251 NotAvailable = -1992isize,
252 #[error("Incompatible SDK Version")]
254 IncompatibleVersion = -1991isize,
255 #[error("The Wooting Analog SDK could not be found on the system")]
257 DLLNotFound = -1990isize,
258}
259
260impl WootingAnalogResult {
261 pub fn is_ok(&self) -> bool {
262 *self == WootingAnalogResult::Ok
263 }
264
265 pub fn is_ok_or_no_device(&self) -> bool {
266 *self == WootingAnalogResult::Ok || *self == WootingAnalogResult::NoDevices
267 }
268}
269
270impl Default for WootingAnalogResult {
271 fn default() -> Self {
272 WootingAnalogResult::FunctionNotFound
273 }
274}
275
276#[derive(Debug)]
277pub struct SDKResult<T>(pub std::result::Result<T, WootingAnalogResult>);
278
279impl<T> Default for SDKResult<T> {
280 fn default() -> Self {
281 Err(Default::default()).into()
282 }
283}
284
285impl<T> Deref for SDKResult<T> {
286 type Target = std::result::Result<T, WootingAnalogResult>;
287
288 fn deref(&self) -> &Self::Target {
289 &self.0
290 }
291}
292
293impl<T> From<std::result::Result<T, WootingAnalogResult>> for SDKResult<T> {
294 fn from(ptr: std::result::Result<T, WootingAnalogResult>) -> Self {
295 SDKResult(ptr)
296 }
297}
298
299impl<T> Into<std::result::Result<T, WootingAnalogResult>> for SDKResult<T> {
300 fn into(self) -> std::result::Result<T, WootingAnalogResult> {
301 self.0
302 }
303}
304
305impl<'a> From<FfiStr<'a>> for SDKResult<FfiStr<'a>> {
307 fn from(res: FfiStr<'a>) -> Self {
308 Ok(res).into()
309 }
310}
311
312impl From<c_int> for SDKResult<c_int> {
313 fn from(res: c_int) -> Self {
314 if res >= 0 {
315 Ok(res).into()
316 } else {
317 Err(WootingAnalogResult::from_i32(res).unwrap_or(WootingAnalogResult::Failure)).into()
318 }
319 }
320}
321
322impl From<c_int> for SDKResult<u32> {
323 fn from(res: c_int) -> Self {
324 if res >= 0 {
325 Ok(res as u32).into()
326 } else {
327 Err(WootingAnalogResult::from_i32(res).unwrap_or(WootingAnalogResult::Failure)).into()
328 }
329 }
330}
331
332impl Into<c_int> for WootingAnalogResult {
333 fn into(self) -> c_int {
334 self as c_int
335 }
336}
337
338impl From<u32> for SDKResult<u32> {
339 fn from(res: u32) -> Self {
340 Ok(res).into()
341 }
342}
343
344impl Into<i32> for SDKResult<u32> {
345 fn into(self) -> i32 {
346 match self.0 {
347 Ok(v) => v as i32,
348 Err(e) => e.into(),
349 }
350 }
351}
352
353impl Into<c_int> for SDKResult<c_int> {
354 fn into(self) -> c_int {
355 match self.0 {
356 Ok(v) => v,
357 Err(e) => e.into(),
358 }
359 }
360}
361
362impl From<f32> for SDKResult<f32> {
363 fn from(res: f32) -> Self {
364 if res >= 0.0 {
365 Ok(res).into()
366 } else {
367 Err(WootingAnalogResult::from_f32(res).unwrap_or(WootingAnalogResult::Failure)).into()
368 }
369 }
370}
371
372impl Into<f32> for WootingAnalogResult {
373 fn into(self) -> f32 {
374 (self as i32) as f32
375 }
376}
377
378impl Into<f32> for SDKResult<f32> {
379 fn into(self) -> f32 {
380 match self.0 {
381 Ok(v) => v,
382 Err(e) => e.into(),
383 }
384 }
385}
386
387impl Into<WootingAnalogResult> for SDKResult<()> {
388 fn into(self) -> WootingAnalogResult {
389 match self.0 {
390 Ok(_) => WootingAnalogResult::Ok,
391 Err(e) => e,
392 }
393 }
394}
395
396impl From<WootingAnalogResult> for SDKResult<()> {
397 fn from(res: WootingAnalogResult) -> Self {
398 if res.is_ok() {
399 Ok(()).into()
400 } else {
401 Err(res).into()
402 }
403 }
404}
405
406impl Into<bool> for WootingAnalogResult {
407 fn into(self) -> bool {
408 self == WootingAnalogResult::Ok
409 }
410}
411
412#[cfg_attr(feature = "serdes", derive(Serialize, Deserialize))]
413#[derive(Debug, PartialEq, Clone, Hash, Eq, Primitive)]
414#[repr(C)]
415pub enum HIDCodes {
416 A = 0x04,
417 B = 0x05, C = 0x06, D = 0x07, E = 0x08, F = 0x09, G = 0x0a, H = 0x0b, I = 0x0c, J = 0x0d, K = 0x0e, L = 0x0f, M = 0x10, N = 0x11, O = 0x12, P = 0x13, Q = 0x14, R = 0x15, S = 0x16, T = 0x17, U = 0x18, V = 0x19, W = 0x1a, X = 0x1b, Y = 0x1c, Z = 0x1d, N1 = 0x1e, N2 = 0x1f, N3 = 0x20, N4 = 0x21, N5 = 0x22, N6 = 0x23, N7 = 0x24, N8 = 0x25, N9 = 0x26, N0 = 0x27, Enter = 0x28, Escape = 0x29, Backspace = 0x2a, Tab = 0x2b, Space = 0x2c, Minus = 0x2d, Equal = 0x2e, BracketLeft = 0x2f, BracketRight = 0x30, Backslash = 0x31, Semicolon = 0x33, Quote = 0x34, Backquote = 0x35, Comma = 0x36, Period = 0x37, Slash = 0x38, CapsLock = 0x39, F1 = 0x3a, F2 = 0x3b, F3 = 0x3c, F4 = 0x3d, F5 = 0x3e, F6 = 0x3f, F7 = 0x40, F8 = 0x41, F9 = 0x42, F10 = 0x43, F11 = 0x44, F12 = 0x45, PrintScreen = 0x46, ScrollLock = 0x47, PauseBreak = 0x48, Insert = 0x49, Home = 0x4a, PageUp = 0x4b, Delete = 0x4c, End = 0x4d, PageDown = 0x4e, ArrowRight = 0x4f, ArrowLeft = 0x50, ArrowDown = 0x51, ArrowUp = 0x52, NumLock = 0x53, NumpadDivide = 0x54, NumpadMultiply = 0x55, NumpadSubtract = 0x56, NumpadAdd = 0x57, NumpadEnter = 0x58, Numpad1 = 0x59, Numpad2 = 0x5a, Numpad3 = 0x5b, Numpad4 = 0x5c, Numpad5 = 0x5d, Numpad6 = 0x5e, Numpad7 = 0x5f, Numpad8 = 0x60, Numpad9 = 0x61, Numpad0 = 0x62, NumpadDecimal = 0x63, InternationalBackslash = 0x64, ContextMenu = 0x65, Power = 0x66, NumpadEqual = 0x67, F13 = 0x68, F14 = 0x69, F15 = 0x6a, F16 = 0x6b, F17 = 0x6c, F18 = 0x6d, F19 = 0x6e, F20 = 0x6f, F21 = 0x70, F22 = 0x71, F23 = 0x72, F24 = 0x73, Open = 0x74, Help = 0x75, Again = 0x79, Undo = 0x7a, Cut = 0x7b, Copy = 0x7c, Paste = 0x7d, Find = 0x7e, VolumeMute = 0x7f, VolumeUp = 0x80, VolumeDown = 0x81, NumpadComma = 0x85, InternationalRO = 0x87, KanaMode = 0x88, InternationalYen = 0x89, Convert = 0x8a, NonConvert = 0x8b, Lang1 = 0x90, Lang2 = 0x91, Lang3 = 0x92, Lang4 = 0x93, LeftCtrl = 0xe0, LeftShift = 0xe1, LeftAlt = 0xe2, LeftMeta = 0xe3, RightCtrl = 0xe4, RightShift = 0xe5, RightAlt = 0xe6, RightMeta = 0xe7, }