Add naive Compose button impl

This commit is contained in:
2024-02-04 15:28:25 +01:00
parent 7f6971b329
commit abb4df15f0
7 changed files with 148 additions and 89 deletions

1
Cargo.lock generated
View File

@ -1532,7 +1532,6 @@ dependencies = [
[[package]] [[package]]
name = "tgnt" name = "tgnt"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.nubo.sh/hulthe/tgnt.git#c34c317f4e189c9937ea6da1342272efd86ad88d"
dependencies = [ dependencies = [
"serde", "serde",
] ]

View File

@ -1,3 +1,6 @@
[patch.'https://git.nubo.sh/hulthe/tgnt.git'.tgnt]
path = "../tgnt"
[workspace] [workspace]
members = [ members = [
"lib", "lib",

View File

@ -49,6 +49,7 @@ pub mod button {
ReleaseKey(Key), ReleaseKey(Key),
PressMod(Modifier), PressMod(Modifier),
ReleaseMod(Modifier), ReleaseMod(Modifier),
Wait,
} }
} }

View File

@ -145,7 +145,7 @@ async fn handle_event(
Button::Key(..) => LightState::Solid(Rgb::new(0, 150, 0)), Button::Key(..) => LightState::Solid(Rgb::new(0, 150, 0)),
Button::Mod(..) => LightState::Solid(Rgb::new(0, 0, 150)), Button::Mod(..) => LightState::Solid(Rgb::new(0, 0, 150)),
Button::ModTap(..) => 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::Layer(..) => LightState::Solid(Rgb::new(120, 0, 120)),
/* /*
Button::NextLayer | Button::PrevLayer => { Button::NextLayer | Button::PrevLayer => {

View File

@ -5,14 +5,19 @@ use embassy_time::{Duration, Instant, Timer};
use futures::FutureExt; use futures::FutureExt;
use heapless::Deque; use heapless::Deque;
use log::{debug, error}; use log::{debug, error};
use tgnt::button::Button; use tgnt::{button::Button, keys::Key};
use crate::event::{button, switch, Half}; 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); const MOD_TAP_TIME: Duration = Duration::from_millis(150);
/// The number of switches on this keyboard half.
const SWITCH_COUNT: usize = 18; 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. /// This function perpetually converts between [switch::Event]s and [button::Event]s.
/// ///
/// Call it from a dedicated task. /// Call it from a dedicated task.
@ -36,6 +41,46 @@ pub async fn keypress_handler(
let event = match button { let event = match button {
&Button::Mod(m) | &Button::ModTap(_, m) => button::Event::PressMod(m), &Button::Mod(m) | &Button::ModTap(_, m) => button::Event::PressMod(m),
&Button::Key(k) => button::Event::PressKey(k), &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, _ => return,
}; };
output.publish_immediate(event); output.publish_immediate(event);
@ -140,7 +185,10 @@ pub async fn keypress_handler(
// add event to queue // add event to queue
insert(queue, button); insert(queue, button);
} }
Button::Mod(..) | Button::Key(..) => { Button::Mod(..)
| Button::Key(..)
| Button::Compose2(..)
| Button::Compose3(..) => {
if queue.is_empty() { if queue.is_empty() {
debug!("sending key now"); debug!("sending key now");
// otherwise, send immediately // otherwise, send immediately
@ -151,15 +199,6 @@ pub async fn keypress_handler(
insert(queue, button); 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; 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 // if this press event was in queue, resolve all ModTaps before in queue as Mods
// otherwise, just resolve this // otherwise, just resolve this
if let Some(position_in_queue) = position_in_queue { if let Some(position_in_queue) = position_in_queue {
@ -209,11 +251,6 @@ pub async fn keypress_handler(
debug!("releasing key {button:?}"); debug!("releasing key {button:?}");
slow_release(output, &button).await; 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
}
_ => {} _ => {}
} }
} }

View File

@ -1,12 +1,13 @@
pub mod report; pub mod report;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_futures::select::select; use embassy_futures::select::{select, Either};
use embassy_rp::{peripherals::USB, usb::Driver}; use embassy_rp::{peripherals::USB, usb::Driver};
use embassy_sync::{ use embassy_sync::{
blocking_mutex::raw::NoopRawMutex, blocking_mutex::raw::NoopRawMutex,
mutex::Mutex, mutex::Mutex,
pubsub::{PubSubChannel, WaitResult}, pubsub::{subscriber::Sub, PubSubBehavior, PubSubChannel, WaitResult},
signal::Signal,
}; };
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use embassy_usb::{ use embassy_usb::{
@ -40,8 +41,12 @@ struct Reports {
} }
struct Context { struct Context {
reports: Mutex<CS, Reports>,
/// Signalled by [report_task] when a report is sent.
report_signal: Signal<CS, ()>,
handler: Handler, handler: Handler,
state: hid::State<'static>,
} }
/// Set up a USB HID keyboard. This function panics if called more than once. /// 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<Context> = StaticCell::new(); static CONTEXT: StaticCell<Context> = StaticCell::new();
// this panics if the functon is called twice // this panics if the functon is called twice
CONTEXT.init(Context { CONTEXT.init(Context {
reports: Mutex::new(Reports {
actual: EMPTY_KEYBOARD_REPORT,
unsent: EMPTY_KEYBOARD_REPORT,
}),
report_signal: Signal::new(),
handler: Handler, handler: Handler,
state: hid::State::new(),
}) })
}; };
let reports = { let hid_state = {
static KB_REPORT: Mutex<CS, Reports> = Mutex::new(Reports { static HID_STATE: StaticCell<hid::State<'static>> = StaticCell::new();
actual: EMPTY_KEYBOARD_REPORT, // this panics if the functon is called twice
unsent: EMPTY_KEYBOARD_REPORT, HID_STATE.init(hid::State::new())
});
&KB_REPORT
}; };
let config = hid::Config { 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, 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; let spawner = Spawner::for_current_executor().await;
spawner.must_spawn(report_task(stream, &context.handler, reports)); spawner.must_spawn(report_task(stream, context));
spawner.must_spawn(event_listener_task(events, reports)); spawner.must_spawn(event_listener_task(events, context));
log::info!("done setting up usb keyboard"); log::info!("done setting up usb keyboard");
} }
@ -110,78 +117,90 @@ impl RequestHandler for Handler {
type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>; type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>;
#[embassy_executor::task] #[embassy_executor::task]
async fn event_listener_task(mut events: KbEvents, reports: &'static Mutex<CS, Reports>) { async fn event_listener_task(mut events: KbEvents, ctx: &'static Context) -> ! {
let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new(); let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new();
let mut button_pub = button_events.publisher().unwrap(); let mut button_pub = button_events.publisher().unwrap();
let mut button_sub = button_events.subscriber().unwrap(); let mut button_sub = button_events.subscriber().unwrap();
select( let r = select(
async { button_events_to_report(ctx, &mut *button_sub),
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;
}
}
},
keypress_handler(&mut *events.subscriber, &mut *button_pub), keypress_handler(&mut *events.subscriber, &mut *button_pub),
) )
.await; .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>, 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] #[embassy_executor::task]
async fn report_task( async fn report_task(stream: HidStream, ctx: &'static Context) {
stream: HidStream, if let Err(e) = write_reports(stream, ctx).await {
handler: &'static Handler,
reports: &'static Mutex<CS, Reports>,
) {
if let Err(e) = keyboard_report(stream, handler, reports).await {
log::error!("keyboard error: {e:?}"); log::error!("keyboard error: {e:?}");
} }
} }
async fn keyboard_report( /// Write keyboard reports.
mut stream: HidStream, async fn write_reports(mut stream: HidStream, ctx: &'static Context) -> Result<(), Error> {
_handler: &'static Handler,
reports: &'static Mutex<CS, Reports>,
) -> Result<(), Error> {
stream.ready().await; stream.ready().await;
loop { loop {
Timer::after(Duration::from_millis(2)).await; Timer::after(Duration::from_millis(2)).await;
let report = { 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.unsent = EMPTY_KEYBOARD_REPORT;
reports.actual reports.actual
}; };

View File

@ -32,14 +32,14 @@
Layer( Layer(
buttons: [ buttons: [
// Row 1 // Row 1
Compose(O, A, None), // å Compose2(Lower, O, Variable, A), // å
Key(D7), Key(D7),
Key(D8), Key(D8),
Key(D9), Key(D9),
Key(D0), Key(D0),
// Row 2 // Row 2
Compose(Apostrophe, A, None), // ä Compose2(Lower, Apostrophe, Variable, A), // ä
ModTap(D4, RCtrl), ModTap(D4, RCtrl),
ModTap(D5, RShift), ModTap(D5, RShift),
ModTap(D6, RAlt), ModTap(D6, RAlt),
@ -47,7 +47,7 @@
Mod(RMod), Mod(RMod),
// Row 3 // Row 3
Compose(Apostrophe, A, None), // ö Compose2(Lower, Apostrophe, Variable, O), // ö
Key(D1), Key(D1),
Key(D2), Key(D2),
Key(D3), Key(D3),