aboutsummaryrefslogtreecommitdiffstats
path: root/src/ffi.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ffi.rs')
-rw-r--r--src/ffi.rs245
1 files changed, 245 insertions, 0 deletions
diff --git a/src/ffi.rs b/src/ffi.rs
new file mode 100644
index 0000000..f3175b7
--- /dev/null
+++ b/src/ffi.rs
@@ -0,0 +1,245 @@
+use std::env;
+use std::ffi::{CStr, CString, OsString};
+use std::fs;
+use std::process::Command;
+use std::ptr;
+use std::slice;
+
+use libc::{c_char, size_t};
+use stderrlog;
+use xdg;
+
+use {Action, HeadphoneButton, MapAction, MapGroup, MapKind};
+use config::{self, Config};
+use trial;
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct Trigger {
+ pub buttons: *const HeadphoneButton,
+ pub length: size_t,
+}
+
+#[repr(C)]
+pub enum ActionKind {
+ Map,
+ Command,
+ Mode,
+}
+
+#[derive(Default)]
+pub struct State {
+ in_mode: Option<Vec<HeadphoneButton>>,
+ map_group: Option<MapGroup>,
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_logger_init() {
+ stderrlog::new()
+ .module(module_path!())
+ .color(stderrlog::ColorChoice::Never)
+ .timestamp(stderrlog::Timestamp::Millisecond)
+ .init()
+ .unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_state_new() -> *mut State {
+ Box::into_raw(Box::new(State::default()))
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_state_free(ptr: *mut State) {
+ if ptr.is_null() { return }
+ unsafe { Box::from_raw(ptr); }
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_state_load_map_group(ptr: *mut State) {
+ match xdg::BaseDirectories::with_prefix("dome-key") {
+ Ok(xdg_dirs) => {
+ match xdg_dirs.find_config_file("mappings.dkmap") {
+ Some(mapping_file) => {
+ let state = unsafe {
+ assert!(!ptr.is_null());
+ &mut *ptr
+ };
+
+ let dkmap = fs::read_to_string(mapping_file)
+ .expect("Failed to read 'mappings.dkmap'");
+
+ let mut map_group = MapGroup::parse(&dkmap)
+ .expect("Failed to parse 'mappings.dkmap'");
+ map_group.parse_actions();
+
+ state.map_group = Some(map_group);
+ },
+ None => {
+ match xdg_dirs.get_config_home().to_str() {
+ Some(config_home) => {
+ error!(
+ "No mapping file found at '{}{}'",
+ config_home,
+ "mappings.dkmap"
+ )
+ },
+ None => {
+ error!("Config home path contains invalid unicode")
+ }
+ }
+ },
+ }
+ },
+ Err(e) => error!("{}", e),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_run_key_action(
+ state: *mut State,
+ trigger: Trigger,
+) {
+ let trigger = unsafe {
+ assert!(!trigger.buttons.is_null());
+
+ slice::from_raw_parts(trigger.buttons, trigger.length as usize)
+ };
+
+ let mut state = unsafe {
+ assert!(!state.is_null());
+ &mut *state
+ };
+
+ run_key_action_for_mode(&mut state, trigger);
+}
+
+// TODO: un-extern
+#[no_mangle]
+pub extern "C" fn run_key_action_for_mode<'a>(
+ state: &mut State,
+ trigger: &'a [HeadphoneButton],
+) {
+ match state.map_group {
+ Some(ref map_group) => {
+ let map = map_group.maps.get(trigger);
+ let mode = map_group.modes.get(trigger);
+
+ if let Some(in_mode) = state.in_mode.clone() {
+ if let Some(mode) = map_group.modes.get(&in_mode) {
+ // Deactivate mode by pressing current mode trigger
+ if &in_mode[..] == trigger {
+ state.in_mode = None;
+
+ return;
+ }
+
+ if let Some(map) = mode.get(trigger) {
+ run_action(&map);
+ }
+ }
+ }
+
+ // TODO: make sure this doesn't run when in_mode
+ if state.in_mode.is_none() {
+ if let Some(map) = map {
+ run_action(&map);
+ }
+ }
+
+ if mode.is_some() {
+ state.in_mode = Some(trigger.to_vec());
+ }
+ },
+ None => (),
+ }
+}
+
+fn run_action(map_action: &MapAction) {
+ match map_action.kind {
+ MapKind::Map => {
+ if let Action::Map(action) = &map_action.action {
+ for key in action {
+ key.tap()
+ }
+ }
+ },
+ MapKind::Command => {
+ if let Action::String(action) = &map_action.action {
+ let shell = match env::var_os("SHELL") {
+ Some(s) => s,
+ None => OsString::from("/bin/sh"),
+ };
+
+ match Command::new(shell)
+ .arg("-c")
+ .arg(action)
+ .spawn() {
+ Ok(_) => (),
+ Err(e) => error!(
+ "Command failed to start: `{}'",
+ e
+ ),
+ }
+ }
+ },
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_parse_args(
+ args: *const *const c_char,
+ length: size_t,
+ config_ptr: *mut Config
+) -> *mut Config {
+ let args = unsafe {
+ assert!(!args.is_null());
+
+ let args = slice::from_raw_parts(args, length as usize);
+
+ args
+ .iter()
+ .map(|s| {
+ assert!(!s.is_null());
+
+ CStr::from_ptr(*s)
+ .to_string_lossy()
+ .into_owned()
+ })
+ .collect::<Vec<String>>()
+ };
+
+ let config = unsafe {
+ assert!(!config_ptr.is_null());
+
+ &mut *config_ptr
+ };
+ config::parse_args(&args, config);
+
+ config_ptr
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_config_get() -> *mut Config {
+ match config::get_config() {
+ Ok(config) => Box::into_raw(Box::new(config)),
+ Err(e) => {
+ error!("{}", e);
+
+ ptr::null_mut()
+ },
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_config_free(ptr: *mut Config) {
+ if ptr.is_null() { return }
+ let config = unsafe { Box::from_raw(ptr) };
+
+ if config.args.license.is_null() { return }
+ unsafe { CString::from_raw(config.args.license); }
+}
+
+#[no_mangle]
+pub extern "C" fn dome_key_do_trial() {
+ trial::do_trial();
+}