aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib.rs
AgeCommit message (Collapse)Author
2018-10-13Try to send media key events using `NSEvent` and `CGevent`Teddy Wing
Segfaults on key_code.rs:184: error: process didn't exit successfully: `.../dome-key-map/target/debug/deps/dome_key_map-0efa5c8428fad354 send_media_key --nocapture` (signal: 11, SIGSEGV: invalid memory reference) shell returned 101 This is my attempt to use first the 'cocoa' and 'core-graphics' crates, then the 'objc' crate (because I hoped that would get around the segfault), to simulate a media key event. Once I got the types right and the code compiling, I couldn't get past segfaults. After struggling with this for over a day and not being able to figure out what on earth is going on, I wrote the exact same thing in Objective-C and it just worked. That's it, I'm done with this. This code is going to be expunged and I'm going to take a C function pointer in the function that runs map actions that will simulate media key presses in real Cocoa/Carbon. Enough of this headache.
2018-10-11Add a function to send media key eventsTeddy Wing
Media keys (rewind, play/pause, fast forward on Mac function keys) are different from normal keys. These use a different API, and sending events to simulate those keys is different. Here's a first stab at getting something working for posting simulated media key events. This will be used for new special keys in mapping actions. Haven't tested this at all yet, just happy that it finally compiles. Follow the two Stack Overflow answers referenced in the comments (from Albert https://stackoverflow.com/users/133374/albert and Nick Sweeting https://stackoverflow.com/users/2156113/nick-sweeting). Add the `core-graphics` crate to give us access to `CGEvent`, `CGKeyCode`, and related types. Also include some commented `CGKeyCode` definitions for future use.
2018-10-05Add a command line option parserTeddy Wing
Parse command line options in this library. Need to figure out how to communicate these over FFI to the Objective-C code.
2018-10-03run_key_action_for_mode(): Extract action code to a new methodTeddy Wing
Move the key simulation and command execution code to a new method so that it can be reused in both top-level map and mode branches. This additionally enables command running from top-level maps (previously it only worked for in-mode maps).
2018-09-30parser: Add `key_code` and `special_key` parsersTeddy Wing
Still need to test them though. They'll be used in conjunction with another new parser, `action_map`, which will parser map-type action strings. `key_code` parses `autopilot::key::KeyCode`s, basically all special keys and modifiers. `special_key` parses key codes inside `<` `>` braces. Change the test to use `easy_parse` to give a clear error message. Add `recursion_limit` to the crate on the advice of the compiler in order for the `choice!` macro in `key_code()` to work properly. Otherwise it spits out a giant error message complaining about `rustc --explain E0277`. If I remove a handful of elements from the `choice!`, it compiles, so it seems there's an upper bound restricted by the recursion limit. Modifier keys are included in a commented section as these are part of the 'autopilot' `KeyCode` enum, but they'll be handled in a separate parser.
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-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-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-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-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-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-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.
2018-08-22Add `MapGroup::parse()`; Start Cocoa functionTeddy Wing
Add a new `cocoa` module which will hold code to be called from Objective-C. Currently contains a sketch of a function that will be called via FFI from Objective-C. Haven't thought of a name for it yet. For now contains a sample map definition "file" in a hard-coded string. Tells `MapGroup` to parse the map definitions. Will try to create some Cocoa objects in the `unsafe` block. `MapGroup`: Add `parse()` method. Given a map definition string, this new function will parse the string into a `MapGroup` and return the result. It will serve as the entry point to parsing DomeKey map definitions. Thanks to these references for Combine parser error handling: - https://github.com/ordian/toml_edit/blob/b02bc3/src/parser/document.rs - https://github.com/ordian/toml_edit/blob/ee6895f/src/parser/errors.rs - https://github.com/Marwes/combine/blob/54bcfed/examples/ini.rs
2018-08-22Move all code in `lib.rs` to `parser.rs`Teddy Wing
Put our parsing code in its own module. We'll be creating a new `cocoa` module and doing this allows us to keep `lib` free as a module container.
2018-08-20Parse whole map file stringsTeddy Wing
Very important: file must be terminated by a newline in order to parse correctly because of the way `Action`s are parsed. Will need to correct this to work for EOF also. We can now parse a whole file of map and mode definitions!
2018-08-20definitions(): Get rid of failed implementation attemptsTeddy Wing
Clear out the commented failed attempts from e96b453f33345ba08c8150192157077f310f1b01.
2018-08-20Parse multiple maps and modes intermixedTeddy Wing
Create a new `Definition` item type that groups `Map` and `Mode` into a single type that we can expect on a parser. This allows us to make a parser that can parse both modes & maps. A bunch of failed attempts, but finally managed to get something working, yay!
2018-08-20Try to parse a `MapGroup`Teddy Wing
No dice. Getting this error: thread 'tests::map_group_parses_a_whole_map_file_string' panicked at 'assertion failed: `(left == right)` left: `Err(Errors { position: PointerOffset(4376730950), errors: [Unexpected(Token('a')), Expected(Borrowed("mode"))] })`, right: `Ok(MapGroup { maps: {[Down]: MapAction { action: "/bin/echo nothing", kind: Command }, [Play]: MapAction { action: "/usr/bin/say hello", kind: Command }}, modes: {[Down, Up]: {[Play]: MapAction { action: "p", kind: Map }}} })`', src/lib.rs:421:9 I need a way to parse modes and maps in any order. Here the code is trying to parse first modes, then maps.
2018-08-19Parse modesTeddy Wing
Add a new `Mode` type to parse into, similar to the `Map` type we made recently. Parse a mode definition, which contains a `MapCollection`. Modes cannot contain other modes, so we don't bother worrying about recursive parsers.
2018-08-19map_collection(): Handle whitespace between map definitionsTeddy Wing
Add support for blank, whitespace, and comment lines between and around map definition lines. Fiddled with the existing functions I had for this, but finally managed to get it working, still using Combine's `examples/ini.rs` as a reference.
2018-08-17Parse a single-element `MapCollection`Teddy Wing
Managed to get `MapCollection` parsing working, but only for a single mapping for now. Need to figure out what I'm doing wrong. Here's the result of my test: thread 'tests::map_collection_parses_maps' panicked at 'assertion failed: `(left == right)` left: `Ok({[Up, Down]: MapAction { action: "test", kind: Map }})`, right: `Ok({[Down]: MapAction { action: "/usr/bin/say \'hello\'", kind: Command }, [Up, Down]: MapAction { action: "test", kind: Map }})`', src/lib.rs:265:9 note: Run with `RUST_BACKTRACE=1` for a backtrace. test tests::map_collection_parses_maps ... FAILED At first the assertion in the test wasn't working, failing to compile with this error: error[E0369]: binary operation `==` cannot be applied to type `std::collections::HashMap<std::vec::Vec<HeadphoneButton>, MapAction>` --> src/lib.rs:266:9 | 266 | assert_eq!(result.unwrap(), expected); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: an implementation of `std::cmp::PartialEq` might be missing for `std::collections::HashMap<std::vec::Vec<HeadphoneButton>, MapAction>` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) Finally realised that I needed to derive `PartialEq` on `MapAction` to make it work. Added a comment parser, based on the `examples/ini.rs` example from Combine: fn whitespace<I>() -> impl Parser<Input = I> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { let comment = (token(';'), skip_many(satisfy(|c| c != '\n'))).map(|_| ()); // Wrap the `spaces().or(comment)` in `skip_many` so that it skips alternating whitespace and // comments skip_many(skip_many1(space()).or(comment)) } Tried writing a test for `comment()`, but couldn't get it to work, presumably because `comment()` doesn't have any output: #[test] fn comment_parses_line_comments() { let text = "# This is a comment "; let result = comment().parse(text).map(|t| t.0); // print!("{:?}", result); assert_eq!(result, Ok(())); }
2018-08-16Parse single map definitionsTeddy Wing
Parse a map definition line into a `Map`. Got so stuck on this error: error[E0277]: the trait bound `(): combine::Parser` is not satisfied --> src/lib.rs:107:16 | 107 | fn map<I>() -> impl Parser<Input = I, Output = Map> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `combine::Parser` is not implemented for `()` | = 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 E0277`. Turns out the problem was that I had put a semicolon at the end of the expression in `map()`. Geez. Took hours to finally see that.
2018-08-16New `Map` struct; Rename existing `Map` struct to `MapAction`Teddy Wing
Add a new `Map` type that includes a field for `trigger`. This allows us to parse a single map definition line into a single struct, making parsing more granular than going directly to `MapCollection`. Rename the existing `Map` type to `MapAction` to support this change.
2018-08-16Try to parse a `MapCollection`Teddy Wing
Can't get this to work. Right now I'm getting this error when running my test: Err(Errors { position: PointerOffset(4522385157), errors: [Unexpected(Token('m')), Expected(Borrowed("lf newline")), Expected(Borrowed("whitespace")), Expected(Borrowed("tab"))] }) It's supposed to parse a set of map lines, including whitespace and comments, into a `MapCollection`. Right now I'm thinking I should split this up more and make a more fine-grained parser for just a single map line, then build up to the collection from there.
2018-08-09Add parser for map actionTeddy Wing
For now just parses a string to a newline. In the future this will have to be more complicated to deal with `<...>`-style special characters and escaping (darn).
2018-08-09Ignore case when parsing `HeadphoneButton`Teddy Wing
Should work with `<play>`, `<Play>`, `<PLAY>`, `<PlAy>`.
2018-08-09Parse a `Trigger`, or sequence of `HeadphoneButton`sTeddy Wing
So cool! I love parser combinators.
2018-08-09Parse headphone buttons (`<play>`, `<up>`, `<down>`)Teddy Wing
Parses a headphone button into the `HeadphoneButton` enum.
2018-08-09map_kind(): Remove old commented codeTeddy Wing
These were early tests to figure out how to parse my tokens.
2018-08-09Parser for `map` and `cmd` keywordsTeddy Wing
This parser reads those tokens and parses them to `MapKind` types. Took a bit of struggling and researching, but it's so cool to see it working now!
2018-08-08Add `MapCollection` type aliasTeddy Wing
Further cleans up the `HashMap` definition in `DKMapGroup`.
2018-08-08Make `Trigger` a list of headphone buttonsTeddy Wing
Cleans up the `HashMap` definition in `DKMapGroup`.
2018-08-08Make `Trigger` an enum instead of a `String`Teddy Wing
`Trigger` can only be one of three values, so it should really be an enum. But of course, the actual trigger is a set of these buttons, so we want a list of trigger buttons as the map hash keys.
2018-08-08Make map `kind` an enum; Fix `modes` typeTeddy Wing
The `kind` has a specific set of things that it can be. It didn't need to be a `String`. Fix `DKMapGroup.modes` to ensure that values are map hashes. Remove `trigger` from `Map` because it didn't make sense to include it in the `HashMap` value if it's the hash key.
2018-08-08Add some types to use for storing the parsed resultTeddy Wing
Imagining how the data should be stored after parsing the map file.
2018-08-08Initialise new project with CargoTeddy Wing
$ cargo init --lib $ rustc --version rustc 1.28.0 (9634041f0 2018-07-30)