aboutsummaryrefslogtreecommitdiffstats
path: root/src
AgeCommit message (Collapse)Author
2018-09-29cocoa_bridge: Get rid of unused testTeddy Wing
There's no longer a `parse_mappings()` function (did I write one of those?), which causes this test to error on compilation. Remove it since it's just a stub for an old model of the code.
2018-09-29parser: Correct tests to use new `Action` enum typeTeddy Wing
Fix these tests that were based on the `String` version of `Action`, before b02e7366c3c4b9edb5afa0d012952fad369b66a9.
2018-09-29Change `parser::Action` to an enumTeddy Wing
It will now be a different type of `Vec` depending on whether it represents a string map or a command. We'll have new parsers to parse the action definition in a more fine-grained way. The `String` variant is just there for temporary backward compatibility while I figure out parsing for the other two. Still need to update the tests for this change.
2018-09-29Revert "Try to propagate KeyCodeConvertible from Action to everywhere"Teddy Wing
This reverts commit 16cd3895f7b111544927d71904aab912d9abbf59. See that commit message for details.
2018-09-29Try to propagate KeyCodeConvertible from Action to everywhereTeddy Wing
Such a pain. As soon as I clear one set of compilation errors, another set crops up. The last one was like the following: error[E0277]: the trait bound `K: std::default::Default` is not satisfied --> src/cocoa_bridge.rs:117:1 | 117 | / pub extern "C" fn state_new<K>() -> *mut State<K> 118 | | where K: KeyCodeConvertible { 119 | | Box::into_raw(Box::new(State::default())) 120 | | } | |_^ the trait `std::default::Default` is not implemented for `K` | = help: consider adding a `where K: std::default::Default` bound note: required by `cocoa_bridge::State` --> src/cocoa_bridge.rs:100:1 | 100 | / pub struct State<K: KeyCodeConvertible> 101 | | where K: Default { 102 | | in_mode: Option<Vec<HeadphoneButton>>, 103 | | map_group: Option<MapGroup<K>>, 104 | | } | |_^ error[E0277]: the trait bound `K: std::default::Default` is not satisfied I'm done with this. Just going to make a darn enum of 'autopilot's `Character` and `KeyCode` structs so I don't have to deal with this mess.
2018-09-28Revert "parser::map(): Try to parse Action with a dependency on MapKind"Teddy Wing
This reverts commit 76ab45d4a5890c4c348b33c32775e45a7c320c58. See that commit for details.
2018-09-28parser::map(): Try to parse Action with a dependency on MapKindTeddy Wing
Some non-working code where I was trying to get an `Action2` to parse correctly, with a dependency on the result of the `map_kind()` parser. Couldn't figure out how to get this to work though, as I couldn't escape errors like this one: error[E0271]: type mismatch resolving `<[closure@src/parser.rs:157:36: 186:6] as std::ops::FnOnce<(_,)>>::Output == std::result::Result<parser::Map, <I as combine::StreamOnce>::Error>` --> src/parser.rs:137:16 | 137 | fn map<I>() -> impl Parser<Input = I, Output = Map> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `parser::Map` | = note: expected type `std::result::Result<std::result::Result<(parser::Map, _), _>, _>` found type `std::result::Result<parser::Map, _>` = note: required because of the requirements on the impl of `combine::Parser` for `combine::combinator::FlatMap<combine::combinator::TakeUntil<_, combine::char::Newline<I>>, [closure@src/parser.rs:157:36: 186:6]>` = note: the return type of a function must have a statically known size error: aborting due to previous error For more information about this error, try `rustc --explain E0271`. Feeling like I've spent way too long stuck on this, so I'm just going to parse actions into new action type (`Action2` for now) by doing a second parsing pass. It's not going to be as performant, but at least I'm confident that will work. We can always come back to this later. I'll be reverting this commit.
2018-09-27parser: Make a new `Action` typeTeddy Wing
With this new type, I'm trying to set up a way to parse actions more precisely, to allow us to get special keys and modifier-flagged keys. Still trying to work out how this is going to work at the parser level though, since the action parser is going to need to depend on the map kind parser.
2018-09-25logger_init(): Configure `stderrlog`Teddy Wing
* No colours * Add timestamp
2018-09-25Add `logger_init()` to init 'stderrlog'Teddy Wing
Trying to see if this works. If I call the init function over FFI and then call functions that use the error macro, will logging work?
2018-09-25state_load_map_group(): Log errors instead of panickingTeddy Wing
This allows us to log the errors without exiting the program.
2018-09-25state_load_map_group(): Fix double slash in error messageTeddy Wing
2018-09-25run_key_action_for_mode(): Fix bugs from recent state & autopilot changeTeddy Wing
* Use `state.in_mode` as `in_mode` will always be `None` now * Use `type_string` for map actions inside a mode. Otherwise the action wouldn't be typed.
2018-09-25run_key_action_for_mode(): Get `MapGroup` from stateTeddy Wing
Now that there can be a `MapGroup` in the `State`, get it from there. This will allow us to get the `MapGroup` from a configuration file.
2018-09-25Add `state_load_map_group()` to get `MapGroup` from config fileTeddy Wing
Currently untested. This function is supposed to get a mapping file from the XDG config directory, parse it, and load it into the state.
2018-09-24run_key_action_for_mode(): Simulate key press on `map` type actionTeddy Wing
For now this is just a test and doesn't handle modifier keys. Just trying to see if 'autopilot' will work.
2018-09-24run_key_action_for_mode(): Deactivate current mode using triggerTeddy Wing
Enable mode deactivation. If there is a current mode, deactivate it if the trigger pressed is the current mode's trigger. Return a BS `KeyActionResult` just to satisfy the return type requirements of the function. We should really clean this function up and eliminate this return value, but we can't until we move map action key simulator handling into Rust.
2018-09-24run_key_action_for_mode(): Keep track of `in_mode` in `state`Teddy Wing
Instead of letting the Objective-C code keep track of `in_mode` by passing it around in `CKeyActionResult`, keep track of it in a `State` struct. Derive `Clone` on `HeadphoneButton` to resolve this error: error[E0277]: the trait bound `parser::HeadphoneButton: std::clone::Clone` is not satisfied --> src/cocoa_bridge.rs:273:38 | 273 | state.in_mode = Some(trigger.to_vec()); | ^^^^^^ the trait `std::clone::Clone` is not implemented for `parser::HeadphoneButton`
2018-09-22Add a `State` structTeddy Wing
The plan is to use this as an opaque pointer to be passed between the Objective-C code and Rust code. It will contain the current mode (if in a mode) and the parsed `MapGroup` definitions. For now just some code inspired by the Rust FFI Omnibus (http://jakegoulding.com/rust-ffi-omnibus/objects/) to create and free a `State`.
2018-09-06c_run_key_action(): Fix segfault on `in_mode`Teddy Wing
Finally figured out where the problem was with the `in_mode` on my `CKeyActionResult` getting freed too early. It happens because we're trying to use the reference to the `in_mode` slice from the `KeyActionResult` returned by `run_key_action_for_mode()`. We were putting that reference in the `in_mode` of the `CKeyActionResult` returned by `c_run_key_action()`, but it would get freed in that function. In order to make it last indefinitely, we needed to convert it into a raw boxed pointer to prevent Rust from deallocating it. This does mean we now have a memory leak, but we'll just have to make a call into Rust to free it from the C code.
2018-09-06Trying to fix pointer lifetime of mode `CKeyActionResult` (WIP)Teddy Wing
Can't get this working. Tried a bunch of things. Decided to commit this at a stopping point before messing any further. Sort of getting there, with some things that seem like they're pointing in the right direction, but it's not working out so far. Still getting a segfault. Actually, with the `Box::into_raw` addition, the lifetime of the mode result actually lasts for a single call (printf), and then gets freed (weirdly). Makefile: * Add `-W` error flags to see if those would give me any extra information about the pointer problems, but didn't get anything. The fact that I didn't get errors indicated it was a problem on the Rust side. * Change `clean` dependency to `moder.c` so we don't rebuild if the C file (and lib) hasn't changed. c_run_key_action(): * Add some `println!`s and debug clutter * Add a separate `let` binding for `run_key_action_for_mode()` because that seemed to fix some weirdness in control flow when I was debugging the code at one point in `lldb`. Not sure it this change still makes sense to keep. * Changed function/closure version of `in_mode` to `if`/`else` because that may have made a difference in some dumb things I was experimenting with at some point. * Unsuccessful tries of `mem::forget()`, ultimately commented out. * Return a `Box::into_raw` pointer to the `CKeyActionResult` instead of the Rust pointer we were previously returning. This allows the value to live beyond the function and beyond Rust's control into the calling C code. This does, however, mean that the value leaks memory, and will need to be freed manually in the calling C code. run_key_action_for_mode(): * Wrap map handler in a check to verify `in_mode` is `None`. This ensures that if we should be returning a `KeyActionResult` for a mapping inside a mode, we don't run this code that's meant for mappings outside a mode.
2018-09-02Test mode finderTeddy Wing
Add a new `moder.c` based on `includer.c` to test getting a mode and then getting an action for a mode mapping. Doesn't completely work currently. Right now all I get is this output: $ ./moder 2 Segmentation fault: 11 Clearly I'm messing up something with the pointer in `c_run_key_action()`. Not sure what it is yet. Changed `CKeyActionResult.in_mode` to be a `Trigger` instead of a `HeadphoneButton` list. That allows us to pass it directly into the second argument of `c_run_key_action()` without converting. But maybe the pointer value isn't living long enough? Not sure what the problem is yet.
2018-09-02Add `in_mode` to `CKeyActionResult`Teddy Wing
Add a new field for `in_mode` to the result struct. Return it if `run_key_action_for_mode()` gives us a `Some` value for it. Removed the `match` on `action` now that we have a second `Option` type.
2018-09-01run_key_action_for_mode(): Return `KeyActionResult` for modeTeddy Wing
When `trigger` is a mode instead of a map, return a `KeyActionResult` with the `in_mode` field set to `trigger`. The `MapKind` enum didn't really fit with the mode here. I couldn't use `MapKind::{Map,Command}` for this `KeyActionResult` because the trigger is for a mode, which is neither of those two. At first I tried to add `Mode` as an option on `MapKind`, but that would have caused problems. We'd be required to add `MapKind::Mode` as an additional match arm in the two places we match against the kind in `run_key_action_for_mode()`, and in that match arm we'd just have to log an error because a `Mode` kind should never occur in those cases. Instead of having a useless match arm that only serves to log an error, I'd much rather not allow it in the first place. Leaving `MapKind` the same allows us to keep our existing code the way it is without adding unnecessary functionality. In order to be able to declare a type of "Mode", though, I ended up creating a new type that replicates `MapKind` (since I can't just extend the enum to a new type) and adds a `Mode` option. This `ActionKind` is only used for `KeyActionResult`, and `MapKind` is used for `Map`s and `MapAction`s.
2018-09-01parser: Make `Trigger` private againTeddy Wing
This was added for a432dd2824499959635ac9a7cabec25a31dddb14, but since we didn't end up using it, we can revert this. Might end up making the type public again later, but for now it doesn't need to be.
2018-09-01run_key_action_for_mode(): Get rid of old `action` string conversionTeddy Wing
This has been moved to `KeyActionResult::with_action()`.
2018-09-01KeyActionResult: Fix reference/borrow errorsTeddy Wing
Return struct values instead of references in the `KeyActionResult` impl to clear up the borrow errors from a432dd2824499959635ac9a7cabec25a31dddb14. Clean up some of the code. Remove the `build()` method because we no longer need it. The rest of the methods will already return the `KeyActionResult` we need.
2018-09-01KeyActionResult: Make a pseudo-builder for the struct (WIP)Teddy Wing
Make a builder for a new `KeyActionResult`, but without a new "builder" type (seems like I should be able to get away without one in this simple case). I had make `parser::Trigger` public because at first I was using them in `KeyActionResult` for the `in_mode` field. Oh yeah, there's a new `in_mode` field on `KeyActionResult`. We'll be using that to store and pass back the current mode to our FFI caller. This field can be used to call `c_run_key_action()` with that mode scope. Well, the idea is here, but I'm getting move errors: error[E0507]: cannot move out of borrowed content --> src/cocoa_bridge.rs:79:9 | 79 | *self | ^^^^^ cannot move out of borrowed content error[E0596]: cannot borrow immutable borrowed content as mutable --> src/cocoa_bridge.rs:162:30 | 162 | *KeyActionResult::new(MapKind::Map) | ______________________________^ 163 | | .with_action(&map.action) | |_________________________________________________________^ cannot borrow as mutable error[E0507]: cannot move out of borrowed content --> src/cocoa_bridge.rs:162:29 | 162 | / *KeyActionResult::new(MapKind::Map) 163 | | .with_action(&map.action) 164 | | .in_mode(trigger) | |_________________________________________________^ cannot move out of borrowed content Looks like I'll need to change the `KeyActionResult` impl code to pass by value instead of reference. Committing what I have now, though, because it's been a while and it's become kind of a mess.
2018-09-01run_key_action_for_mode(): Start adding mode handlingTeddy Wing
Just the beginning of a way to get a map inside a mode. Not yet complete. We need to modify `KeyActionResult` to give it an `in_mode` field. This will enable us to tell the C code to use that mode scope. Change the `mode` argument to `in_mode` because we already have a local `mode` variable inside the function, and `in_mode` makes sense.
2018-08-29c_run_key_action(): Add `mode` argumentTeddy Wing
Add a nullable `mode` argument like `trigger`. Change `trigger` to make it non-nullable again. We can now pass a mode trigger in from FFI and handle it in our Rust function.
2018-08-29c_run_key_action(): Make `trigger` argument nullableTeddy Wing
Actually, now that I think about it, the trigger argument shouldn't be nullable, since one should always be passed in. But this was really more of a test to make sure we could do the same for a new `mode` argument, which will also be a `Trigger`, but which might be null.
2018-08-29run_key_action(): Add `mode` argumentTeddy Wing
An Option `mode` argument. When `Some`, the function should look for `trigger` inside the given mode. Otherwise it will use the normal map scope.
2018-08-28c_run_key_action(): Change signature to take a `Trigger`Teddy Wing
Our new `Trigger` type groups together the two arguments this function used to take into a single type.
2018-08-28cocoa_bridge: Add a `Trigger` struct to use in `run_key_action()`Teddy Wing
This struct will be used as function arguments to the C version of `run_key_action()`. It will enable us to pass in both a trigger and a mode context more concisely.
2018-08-26c_run_key_action(): Correctly send action string to CTeddy Wing
Using `.as_ptr()` on the `CString` didn't work. The C code wouldn't print anything. Turns out I needed to use `.into_raw()` in order to: > [Consume] the CString and [transfer] ownership of the string to a C > caller. (https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw) This allows us to correctly access and print the string in our C code. Note that we'll still need to free the string using a new Rust function that should be callable from C, otherwise we'll end up with a memory leak.
2018-08-26c_run_key_action(): Get the C code to actually print something outTeddy Wing
Ensure that the C code can really print something sent from the Rust code. Our action string wasn't getting printed out, so I wanted to see it working with a test string. Need to figure out what I have to do to get it to work for real now.
2018-08-26cocoa_bridge(run_key_action): Add newline to end of test map groupTeddy Wing
I keep forgetting to add the newline at the end, darn. Wasted a few minutes of my time, but so cool that it's finally working now! The C program now correctly prints the corresponding map. Here's a diff of print debug statements I used to diagnose the problem: diff --git a/src/cocoa_bridge.rs b/src/cocoa_bridge.rs index fe65533..9690fe4 100644 --- a/src/cocoa_bridge.rs +++ b/src/cocoa_bridge.rs @@ -68,12 +68,14 @@ pub extern "C" fn c_run_key_action( Some(k) => { match k.action { Some(a) => { + println!("all good"); CKeyActionResult { action: a.as_ptr(), kind: &k.kind, } }, None => { + println!("Action null"); CKeyActionResult { action: ptr::null(), kind: &k.kind, @@ -82,6 +84,7 @@ pub extern "C" fn c_run_key_action( } }, None => { + println!("All null"); CKeyActionResult { action: ptr::null(), kind: ptr::null(), @@ -98,11 +101,17 @@ pub extern "C" fn run_key_action( ) -> Option<KeyActionResult> { let sample_maps = "map <up> k map <down> j -map <play><down> works!"; +map <play><down> works! +"; + + println!("{:?}", trigger); + assert!(trigger == &[HeadphoneButton::Play, HeadphoneButton::Down]); // Figure out how to persist this without re-parsing let map_group = MapGroup::parse(sample_maps).unwrap(); + println!("{:?}", map_group.maps); + let map = map_group.maps.get(trigger); let mode = map_group.modes.get(trigger);
2018-08-26Link library with a test C programTeddy Wing
Make a test `includer.c` program that includes the Rust library and calls our `c_run_key_action()` to see if it actually works. Currently it doesn't, we get `(null)` printed to stdout. Add a Makefile with the build command for the C program. cbindgen.toml: Remove `KeyActionResult` from exported types, as the `Option` field it contains caused `gcc` to complain. cocoa_bridge.rs: * Comment out all 'cocoa' crate related code as the 'cocoa' code was interfering with GCC compilation as a result of my not linking to Cocoa frameworks. * Add a new test map definition that corresponds with the one we look for in `includer.c`. parser.rs: Add `#[repr(C)]` to `MapKind` because it needs to be exported in the library and generated into our C header file.
2018-08-26Make data types more C-likeTeddy Wing
Add a new `CKeyActionResult` that uses a `char *` instead of an `Option<CString>`. Make our `c_run_key_action()` return this type instead of `KeyActionResult`. Include some code to handle the translation between the two types. Names for now are just temporary so I can get something working. We'll re-think proper names once I've tested this manually and have something somewhat working.
2018-08-26Get `run_key_action` to export correctly to CTeddy Wing
Add a new wrapper function for `run_key_action` that uses C appropriate inputs & outputs and calls into our Rusty `run_key_action`. This new function now correctly gets a header generated for it by 'cbindgen'. Immense thanks to Jake Goulding on the Rust FFI Omnibus for showing me how to pass a slice argument from C: http://jakegoulding.com/rust-ffi-omnibus/slice_arguments/ In order to pass the slice from C, we need to pass a C array and its length to the function. Cool.
2018-08-25Generate a partial C header for our FFI APITeddy Wing
* Add the 'cbindgen' crate to help us auto-generate a C header for our exported FFI enums/structs/functions * Add a `build.rs` to generate the C header using cbindgen * Add a rough config for 'cbindgen' * Export everything from the `cocoa_bridge` crate to include the `KeyActionResult` struct * Commit the C header generated by 'cbindgen' Looks promising. We do, however, have some things to correct. We can't use `Option` in C, for instance, so we'll need to fix that in our `KeyActionResult`. Similarly, we need to rework the `run_key_action()` function to make it accessible as a C interface. Right now it returns an `Option`, which isn't going to work, and I'm not sure if the slice input translates. That (any maybe more) is why it doesn't get generated by 'cbindgen' into the header output file.
2018-08-25Configure for importing from C/FFITeddy Wing
* Add `#[repr(C)]` on `HeadphoneButton` to hopefully be able to use it outside Rust * Add `#[no_mangle]` to `run_key_action()` * Export `run_key_action()` as a public function * Build the crate as a static library (But holy cow, a 19 MB library file?)
2018-08-25run_key_action(): Include action string for `MapKind::Map`Teddy Wing
Couldn't figure out how to make my `Action` string a char slice. At first I changed it to a `CStr` reference, but ran into lifetime errors when trying to use a reference. Instead ended up making it a `CString`. Still need to handle errors. My plan is to pass back an error string in the result struct. Remains to be seen whether this actually works when calling it over FFI, but we have the beginnings of something.
2018-08-25cocoa_bridge: Comment `run_command()` for nowTeddy Wing
Causes an error as `Action` isn't imported.
2018-08-24run_key_action(): Approach for handling actions for different MapKindsTeddy Wing
Work out the essential body of the function, which necessitates looking inside a `MapGroup` and finding a `MapAction` corresponding to the `Trigger` given in the function argument. The function will then perform whatever action it can and return a `KeyActionResult` struct, allowing callers to pick up the baton.
2018-08-24cocoa_bridge: Idea for black box map run functionTeddy Wing
New `run_key_action()` will be called from Objective C. It will: * Get the action for a `Map` mapping and return it, so that the corresponding keys can be pressed in Cocoa/Carbon * Run a `Command` mapping * Somehow manage mode scoping in conjunction with the Objective C side The new `KeyActionResult` allows us to tell Objective C what type of action should be run (`Map`, `Command`, or `Mode`), along with additional information (for now just the action keys in the case of a `Map` mapping). The question is how to hold on to state, keeping the parsed mappings in memory on the Rust side when called over FFI.
2018-08-24cocoa_bridge(parse_mappings): Ideas for filling the ObjC collectionsTeddy Wing
Not working currently. Just committing this because I didn't before and because I'm working in a different direction now.
2018-08-23cocoa_bridge: Add some ideas for how to make the bridge workTeddy Wing
It's feeling like creating Cocoa objects in Rust is going to be a pain. Thinking I might want to make the API a little more black box-y and do more on the Rust side. These are some notes for ideas about how to do that.
2018-08-23cocoa_bridge: Continue outline of Objective C callableTeddy Wing
Add some more structure to the function that will be called from Objective C code. * Give it a name, `parse_mappings` (not very thoroughly considered) * Initialise some Foundation data structures to mirror our Rust ones * Add the beginning of a struct that will be the ObjC version of our `MapGroup` struct, containing `NSDictionary`ies. * Move the `extern crate` into `lib.rs`, I keep forgetting that's where they go. * Add a test that just calls `parse_mappings` to check that the code compiles. parser::MapGroup: Make both its fields public to enable us to access them from `cocoa_bridge`. Used this helpful example as a reference for the Rust Cocoa code: https://github.com/servo/core-foundation-rs/blob/c99c05c/cocoa/examples/hello_world.rs
2018-08-23Rename my `cocoa` module to `cocoa_bridge`Teddy Wing
My `cocoa` module was conflicting with the `cocoa` crate. Didn't think about that. Rename it to fix the conflict.