diff --git a/Cargo.lock b/Cargo.lock index d32809b..8489bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1532,7 +1532,6 @@ dependencies = [ [[package]] name = "tgnt" version = "0.1.0" -source = "git+https://git.nubo.sh/hulthe/tgnt.git#c34c317f4e189c9937ea6da1342272efd86ad88d" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index c950f09..06c67db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[patch.'https://git.nubo.sh/hulthe/tgnt.git'.tgnt] +path = "../tgnt" + [workspace] members = [ "lib", diff --git a/lib/src/event.rs b/lib/src/event.rs index bfc9498..b755b03 100644 --- a/lib/src/event.rs +++ b/lib/src/event.rs @@ -49,6 +49,7 @@ pub mod button { ReleaseKey(Key), PressMod(Modifier), ReleaseMod(Modifier), + Wait, } } diff --git a/lib/src/keyboard/lights.rs b/lib/src/keyboard/lights.rs index 0fcf824..438abf0 100644 --- a/lib/src/keyboard/lights.rs +++ b/lib/src/keyboard/lights.rs @@ -145,7 +145,7 @@ async fn handle_event( Button::Key(..) => LightState::Solid(Rgb::new(0, 150, 0)), Button::Mod(..) => LightState::Solid(Rgb::new(0, 0, 150)), Button::ModTap(..) => LightState::Solid(Rgb::new(0, 0, 150)), - Button::Compose(..) => LightState::Solid(Rgb::new(0, 100, 100)), + Button::Compose2(..) | Button::Compose3(..) => LightState::Solid(Rgb::new(0, 100, 100)), Button::Layer(..) => LightState::Solid(Rgb::new(120, 0, 120)), /* Button::NextLayer | Button::PrevLayer => { diff --git a/lib/src/keypress_handler.rs b/lib/src/keypress_handler.rs index 7561918..eb3e25c 100644 --- a/lib/src/keypress_handler.rs +++ b/lib/src/keypress_handler.rs @@ -5,14 +5,19 @@ use embassy_time::{Duration, Instant, Timer}; use futures::FutureExt; use heapless::Deque; use log::{debug, error}; -use tgnt::button::Button; +use tgnt::{button::Button, keys::Key}; use crate::event::{button, switch, Half}; -// TODO +/// The time a ModTap button takes to resolve as a Mod while being held down. const MOD_TAP_TIME: Duration = Duration::from_millis(150); + +/// The number of switches on this keyboard half. const SWITCH_COUNT: usize = 18; +/// The usb keycode mapped to Compose. +const COMPOSE_KEY: Key = Key::Application; + /// This function perpetually converts between [switch::Event]s and [button::Event]s. /// /// Call it from a dedicated task. @@ -36,6 +41,46 @@ pub async fn keypress_handler( let event = match button { &Button::Mod(m) | &Button::ModTap(_, m) => button::Event::PressMod(m), &Button::Key(k) => button::Event::PressKey(k), + &Button::Compose2(csa, a, csb, b) => { + let events = [ + button::Event::PressKey(COMPOSE_KEY), + button::Event::ReleaseKey(COMPOSE_KEY), + button::Event::Wait, + button::Event::PressKey(a), + button::Event::ReleaseKey(a), + button::Event::Wait, + button::Event::PressKey(b), + button::Event::ReleaseKey(b), + button::Event::Wait, + ]; + + for event in events { + output.publish_immediate(event); + } + + return; + } + &Button::Compose3(csa, a, csb, b, csc, c) => { + let events = [ + button::Event::PressKey(COMPOSE_KEY), + button::Event::ReleaseKey(COMPOSE_KEY), + button::Event::Wait, + button::Event::PressKey(a), + button::Event::ReleaseKey(a), + button::Event::Wait, + button::Event::PressKey(b), + button::Event::ReleaseKey(b), + button::Event::Wait, + button::Event::PressKey(c), + button::Event::ReleaseKey(c), + ]; + + for event in events { + output.publish_immediate(event); + } + + return; + } _ => return, }; output.publish_immediate(event); @@ -140,7 +185,10 @@ pub async fn keypress_handler( // add event to queue insert(queue, button); } - Button::Mod(..) | Button::Key(..) => { + Button::Mod(..) + | Button::Key(..) + | Button::Compose2(..) + | Button::Compose3(..) => { if queue.is_empty() { debug!("sending key now"); // otherwise, send immediately @@ -151,15 +199,6 @@ pub async fn keypress_handler( insert(queue, button); } } - Button::Compose(..) => { - if queue.is_empty() { - // otherwise, send immediately - // TODO - } else { - // if events in queue, also add to queue - insert(queue, button); - } - } _ => {} } } @@ -193,7 +232,10 @@ pub async fn keypress_handler( slow_release(output, &button).await; }; } - Button::Mod(..) | Button::Key(..) => { + Button::Mod(..) + | Button::Key(..) + | Button::Compose2(..) + | Button::Compose3(..) => { // if this press event was in queue, resolve all ModTaps before in queue as Mods // otherwise, just resolve this if let Some(position_in_queue) = position_in_queue { @@ -209,11 +251,6 @@ pub async fn keypress_handler( debug!("releasing key {button:?}"); slow_release(output, &button).await; } - Button::Compose(..) => { - // if this press event was in queue, resolve all ModTaps before in queue as Mods - // otherwise, just resolve this - // TODO - } _ => {} } } diff --git a/lib/src/usb/keyboard.rs b/lib/src/usb/keyboard.rs index 554a4fe..eb9873b 100644 --- a/lib/src/usb/keyboard.rs +++ b/lib/src/usb/keyboard.rs @@ -1,12 +1,13 @@ pub mod report; use embassy_executor::Spawner; -use embassy_futures::select::select; +use embassy_futures::select::{select, Either}; use embassy_rp::{peripherals::USB, usb::Driver}; use embassy_sync::{ blocking_mutex::raw::NoopRawMutex, mutex::Mutex, - pubsub::{PubSubChannel, WaitResult}, + pubsub::{subscriber::Sub, PubSubBehavior, PubSubChannel, WaitResult}, + signal::Signal, }; use embassy_time::{Duration, Timer}; use embassy_usb::{ @@ -40,8 +41,12 @@ struct Reports { } struct Context { + reports: Mutex, + + /// Signalled by [report_task] when a report is sent. + report_signal: Signal, + handler: Handler, - state: hid::State<'static>, } /// Set up a USB HID keyboard. This function panics if called more than once. @@ -52,17 +57,19 @@ pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>, events: static CONTEXT: StaticCell = StaticCell::new(); // this panics if the functon is called twice CONTEXT.init(Context { + reports: Mutex::new(Reports { + actual: EMPTY_KEYBOARD_REPORT, + unsent: EMPTY_KEYBOARD_REPORT, + }), + report_signal: Signal::new(), handler: Handler, - state: hid::State::new(), }) }; - let reports = { - static KB_REPORT: Mutex = Mutex::new(Reports { - actual: EMPTY_KEYBOARD_REPORT, - unsent: EMPTY_KEYBOARD_REPORT, - }); - &KB_REPORT + let hid_state = { + static HID_STATE: StaticCell> = StaticCell::new(); + // this panics if the functon is called twice + HID_STATE.init(hid::State::new()) }; let config = hid::Config { @@ -73,12 +80,12 @@ pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>, events: max_packet_size: MAX_PACKET_SIZE as u16, }; - let stream = HidStream::new(builder, &mut context.state, config); + let stream = HidStream::new(builder, hid_state, config); let spawner = Spawner::for_current_executor().await; - spawner.must_spawn(report_task(stream, &context.handler, reports)); - spawner.must_spawn(event_listener_task(events, reports)); + spawner.must_spawn(report_task(stream, context)); + spawner.must_spawn(event_listener_task(events, context)); log::info!("done setting up usb keyboard"); } @@ -110,78 +117,90 @@ impl RequestHandler for Handler { type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>; #[embassy_executor::task] -async fn event_listener_task(mut events: KbEvents, reports: &'static Mutex) { +async fn event_listener_task(mut events: KbEvents, ctx: &'static Context) -> ! { let button_events = PubSubChannel::::new(); let mut button_pub = button_events.publisher().unwrap(); let mut button_sub = button_events.subscriber().unwrap(); - select( - async { - loop { - let WaitResult::Message(event) = button_sub.next_message().await else { - error!("lagged"); - continue; - }; - - loop { - let mut r = reports.lock().await; - match event { - button::Event::PressKey(k) => { - r.actual.press_key(k); - r.unsent.press_key(k); - } - button::Event::PressMod(m) => { - r.actual.press_modifier(m); - r.unsent.press_modifier(m); - } - - // we got a key release, but if the key press hasn't been sent yet, we - // wait for a bit until it has. - button::Event::ReleaseKey(k) if r.unsent.key_pressed(k) => { - drop(r); - Timer::after(Duration::from_millis(1)).await; - continue; - } - button::Event::ReleaseMod(m) if r.unsent.modifier_pressed(m) => { - drop(r); - Timer::after(Duration::from_millis(1)).await; - continue; - } - - button::Event::ReleaseKey(k) => r.actual.release_key(k), - button::Event::ReleaseMod(m) => r.actual.release_modifier(m), - } - break; - } - } - }, + let r = select( + button_events_to_report(ctx, &mut *button_sub), keypress_handler(&mut *events.subscriber, &mut *button_pub), ) .await; + + match r { + Either::First(never) | Either::Second(never) => match never {}, + } +} + +/// Listen for [button::Event]s and add them to the keyboard [Reports]. +async fn button_events_to_report( + ctx: &'static Context, + button_sub: &mut Sub<'_, impl PubSubBehavior, button::Event>, +) -> ! { + loop { + let WaitResult::Message(event) = button_sub.next_message().await else { + error!("lagged"); + continue; + }; + + loop { + let mut r = ctx.reports.lock().await; + match event { + button::Event::PressKey(k) => { + r.actual.press_key(k); + r.unsent.press_key(k); + } + button::Event::PressMod(m) => { + r.actual.press_modifier(m); + r.unsent.press_modifier(m); + } + + // we got a key release, but if the key press hasn't been sent yet, we + // wait for a bit until it has. + button::Event::ReleaseKey(k) if r.unsent.key_pressed(k) => { + ctx.report_signal.reset(); + drop(r); + ctx.report_signal.wait().await; + continue; + } + button::Event::ReleaseMod(m) if r.unsent.modifier_pressed(m) => { + ctx.report_signal.reset(); + drop(r); + ctx.report_signal.wait().await; + continue; + } + + button::Event::ReleaseKey(k) => r.actual.release_key(k), + button::Event::ReleaseMod(m) => r.actual.release_modifier(m), + + button::Event::Wait => { + ctx.report_signal.reset(); + drop(r); + ctx.report_signal.wait().await; + } + } + break; + } + } } #[embassy_executor::task] -async fn report_task( - stream: HidStream, - handler: &'static Handler, - reports: &'static Mutex, -) { - if let Err(e) = keyboard_report(stream, handler, reports).await { +async fn report_task(stream: HidStream, ctx: &'static Context) { + if let Err(e) = write_reports(stream, ctx).await { log::error!("keyboard error: {e:?}"); } } -async fn keyboard_report( - mut stream: HidStream, - _handler: &'static Handler, - reports: &'static Mutex, -) -> Result<(), Error> { +/// Write keyboard reports. +async fn write_reports(mut stream: HidStream, ctx: &'static Context) -> Result<(), Error> { stream.ready().await; loop { Timer::after(Duration::from_millis(2)).await; let report = { - let mut reports = reports.lock().await; + let mut reports = ctx.reports.lock().await; + ctx.report_signal.signal(()); reports.unsent = EMPTY_KEYBOARD_REPORT; reports.actual }; diff --git a/right/layers.ron b/right/layers.ron index 19d0875..a506fba 100644 --- a/right/layers.ron +++ b/right/layers.ron @@ -32,14 +32,14 @@ Layer( buttons: [ // Row 1 - Compose(O, A, None), // å + Compose2(Lower, O, Variable, A), // å Key(D7), Key(D8), Key(D9), Key(D0), // Row 2 - Compose(Apostrophe, A, None), // ä + Compose2(Lower, Apostrophe, Variable, A), // ä ModTap(D4, RCtrl), ModTap(D5, RShift), ModTap(D6, RAlt), @@ -47,7 +47,7 @@ Mod(RMod), // Row 3 - Compose(Apostrophe, A, None), // ö + Compose2(Lower, Apostrophe, Variable, O), // ö Key(D1), Key(D2), Key(D3),