1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#[macro_use]
pub extern crate enum_primitive;
#[macro_use]
extern crate log;
extern crate ffi_support;

pub use enum_primitive::FromPrimitive;
use ffi_support::FfiStr;
use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::os::raw::{c_char, c_int};

#[cfg(target_os = "macos")]
pub const DEFAULT_PLUGIN_DIR: &str = "/Library/WootingAnalogPlugins";
#[cfg(target_os = "linux")]
pub const DEFAULT_PLUGIN_DIR: &str = "/usr/local/share/WootingAnalogPlugins";
#[cfg(target_os = "windows")]
pub const DEFAULT_PLUGIN_DIR: &str = "C:\\Program Files\\WootingAnalogPlugins";

/// The core `DeviceInfo` struct which contains all the interesting information
/// for a particular device. This is for use internally and should be ignored if you're
/// trying to use it when trying to interact with the SDK using the wrapper
#[derive(Clone)]
pub struct DeviceInfo {
    /// Device Vendor ID `vid`
    pub vendor_id: u16,
    /// Device Product ID `pid`
    pub product_id: u16,
    /// Device Manufacturer name
    pub manufacturer_name: String,
    /// Device name
    pub device_name: String,
    /// Unique device ID, which should be generated using `generate_device_id`
    pub device_id: DeviceID,
    /// Hardware type of the Device
    pub device_type: DeviceType,
}

/// The core `DeviceInfo` struct which contains all the interesting information
/// for a particular device. This is the version which the consumer of the SDK will receive
/// through the wrapper. This is not for use in the Internal workings of the SDK, that is what
/// DeviceInfo is for
#[repr(C)]
pub struct DeviceInfo_FFI {
    /// Device Vendor ID `vid`
    pub vendor_id: u16,
    /// Device Product ID `pid`
    pub product_id: u16,
    /// Device Manufacturer name
    pub manufacturer_name: *mut c_char,
    /// Device name
    pub device_name: *mut c_char,
    /// Unique device ID, which should be generated using `generate_device_id`
    pub device_id: DeviceID,
    /// Hardware type of the Device
    pub device_type: DeviceType,
}

impl From<DeviceInfo> for DeviceInfo_FFI {
    fn from(device: DeviceInfo) -> Self {
        DeviceInfo_FFI {
            vendor_id: device.vendor_id,
            product_id: device.product_id,
            manufacturer_name: CString::new(device.manufacturer_name).unwrap().into_raw(),
            device_name: CString::new(device.device_name).unwrap().into_raw(),
            device_id: device.device_id,
            device_type: device.device_type,
        }
    }
}

impl Drop for DeviceInfo_FFI {
    fn drop(&mut self) {
        //Ensure we properly drop the memory for the char pointers
        unsafe {
            CString::from_raw(self.manufacturer_name);
            CString::from_raw(self.device_name);
        }
    }
}

impl DeviceInfo {
    //    pub fn new(
    //        vendor_id: u16,
    //        product_id: u16,
    //        manufacturer_name: &str,
    //        device_name: &str,
    //        serial_number: &str,
    //        device_type: DeviceType,
    //    ) -> Self {
    //        DeviceInfo {
    //            vendor_id,
    //            product_id,
    //            manufacturer_name,
    //            device_name,
    //            device_id: generate_device_id(serial_number, vendor_id, product_id),
    //            device_type
    //        }
    //    }

    pub fn new_with_id(
        vendor_id: u16,
        product_id: u16,
        manufacturer_name: String,
        device_name: String,
        device_id: DeviceID,
        device_type: DeviceType,
    ) -> Self {
        DeviceInfo {
            vendor_id,
            product_id,
            manufacturer_name,
            device_name,
            device_id,
            device_type,
        }
    }
}

/// Create a new device info struct. This is only for use in Plugins that are written in C
/// Rust plugins should use the native constructor
/// The memory for the struct has been allocated in Rust. So `drop_device_info` must be called
/// for the memory to be properly released
#[no_mangle]
pub extern "C" fn new_device_info(vendor_id: u16,
                                  product_id: u16,
                                  manufacturer_name: *mut c_char,
                                  device_name: *mut c_char,
                                  device_id: DeviceID,
                                  device_type: DeviceType) -> *mut DeviceInfo {
    Box::into_raw(Box::new(DeviceInfo::new_with_id(vendor_id,
                                                   product_id,
                                                   unsafe { CStr::from_ptr(manufacturer_name).to_string_lossy().into_owned() },
                                                   unsafe { CStr::from_ptr(device_name).to_string_lossy().into_owned() },
                                                   device_id,
                                                   device_type)))
}

/// Drops the given `DeviceInfo`
#[no_mangle]
pub unsafe extern "C" fn drop_device_info(device: *mut DeviceInfo) {
    Box::from_raw(device);
}

enum_from_primitive! {
    #[derive(Debug, PartialEq, Clone)]
    #[repr(C)]
    pub enum KeycodeType {
        /// USB HID Keycodes https://www.usb.org/document-library/hid-usage-tables-112 pg53
        HID,
        /// Scan code set 1
        ScanCode1,
        /// Windows Virtual Keys
        VirtualKey,
        /// Windows Virtual Keys which are translated to the current keyboard locale
        VirtualKeyTranslate
    }
}

pub type DeviceID = u64;

enum_from_primitive! {
    #[derive(Debug, PartialEq, Clone)]
    #[repr(C)]
    pub enum DeviceType  {
        /// Device is of type Keyboard
        Keyboard = 1,
        /// Device is of type Keypad
        Keypad,
        /// Device
        Other
    }
}

enum_from_primitive! {
    #[derive(Debug, PartialEq, Clone)]
    #[repr(C)]
    pub enum DeviceEventType  {
        /// Device has been connected
        Connected = 1,
        /// Device has been disconnected
        Disconnected
    }
}

enum_from_primitive! {
    #[derive(Debug, PartialEq, Clone)]
    #[repr(C)]
    pub enum WootingAnalogResult {
        Ok = 1,
        /// Item hasn't been initialized
        UnInitialized = -2000,
        /// No Devices are connected
        NoDevices,
        /// Device has been disconnected
        DeviceDisconnected,
        /// Generic Failure
        Failure,
        /// A given parameter was invalid
        InvalidArgument,
        /// No Plugins were found
        NoPlugins,
        /// The specified function was not found in the library
        FunctionNotFound,
        /// No Keycode mapping to HID was found for the given Keycode
        NoMapping,
        /// Indicates that it isn't available on this platform
        NotAvailable,
        /// Indicates that the operation that is trying to be used is for an older version
        IncompatibleVersion,

    }
}

impl WootingAnalogResult {
    pub fn is_ok(&self) -> bool {
        *self == WootingAnalogResult::Ok
    }

    pub fn is_ok_or_no_device(&self) -> bool {
        *self == WootingAnalogResult::Ok || *self == WootingAnalogResult::NoDevices
    }
}

impl Default for WootingAnalogResult {
    fn default() -> Self {
        WootingAnalogResult::FunctionNotFound
    }
}

#[derive(Debug)]
pub struct SDKResult<T>(pub std::result::Result<T, WootingAnalogResult>);

impl<T> Default for SDKResult<T> {
    fn default() -> Self {
        Err(Default::default()).into()
    }
}

impl<T> Deref for SDKResult<T> {
    type Target = std::result::Result<T, WootingAnalogResult>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> From<std::result::Result<T, WootingAnalogResult>> for SDKResult<T> {
    fn from(ptr: std::result::Result<T, WootingAnalogResult>) -> Self {
        SDKResult(ptr)
    }
}

impl<T> Into<std::result::Result<T, WootingAnalogResult>> for SDKResult<T> {
    fn into(self) -> std::result::Result<T, WootingAnalogResult> {
        self.0
    }
}

//TODO: Figure out a way to not have to use this for the lib_wrap_option in the sdk
impl<'a> From<FfiStr<'a>> for SDKResult<FfiStr<'a>> {
    fn from(res: FfiStr<'a>) -> Self {
        Ok(res).into()
    }
}

impl From<c_int> for SDKResult<c_int> {
    fn from(res: c_int) -> Self {
        if res >= 0 {
            Ok(res).into()
        } else {
            Err(WootingAnalogResult::from_i32(res).unwrap_or(WootingAnalogResult::Failure)).into()
        }
    }
}

impl Into<c_int> for WootingAnalogResult {
    fn into(self) -> c_int {
        self as c_int
    }
}

impl From<u32> for SDKResult<u32> {
    fn from(res: u32) -> Self {
        Ok(res).into()
    }
}

impl Into<i32> for SDKResult<u32> {
    fn into(self) -> i32 {
        match self.0 {
            Ok(v) => v as i32,
            Err(e) => e.into(),
        }
    }
}

impl Into<c_int> for SDKResult<c_int> {
    fn into(self) -> c_int {
        match self.0 {
            Ok(v) => v,
            Err(e) => e.into(),
        }
    }
}

impl From<f32> for SDKResult<f32> {
    fn from(res: f32) -> Self {
        if res >= 0.0 {
            Ok(res).into()
        } else {
            Err(WootingAnalogResult::from_f32(res).unwrap_or(WootingAnalogResult::Failure)).into()
        }
    }
}

impl Into<f32> for WootingAnalogResult {
    fn into(self) -> f32 {
        (self as i32) as f32
    }
}

impl Into<f32> for SDKResult<f32> {
    fn into(self) -> f32 {
        match self.0 {
            Ok(v) => v,
            Err(e) => e.into(),
        }
    }
}

impl Into<WootingAnalogResult> for SDKResult<()> {
    fn into(self) -> WootingAnalogResult {
        match self.0 {
            Ok(_) => WootingAnalogResult::Ok,
            Err(e) => e,
        }
    }
}

impl<T> From<WootingAnalogResult> for SDKResult<T> {
    fn from(res: WootingAnalogResult) -> Self {
        Err(res).into()
    }
}

impl Into<bool> for WootingAnalogResult {
    fn into(self) -> bool {
        self == WootingAnalogResult::Ok
    }
}