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]]
name = "tgnt"
version = "0.1.0"
source = "git+https://git.nubo.sh/hulthe/tgnt.git#c34c317f4e189c9937ea6da1342272efd86ad88d"
dependencies = [
"serde",
]

View File

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

View File

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

View File

@ -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 => {

View File

@ -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
}
_ => {}
}
}

View File

@ -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<CS, Reports>,
/// Signalled by [report_task] when a report is sent.
report_signal: Signal<CS, ()>,
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<Context> = 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<CS, Reports> = Mutex::new(Reports {
actual: EMPTY_KEYBOARD_REPORT,
unsent: EMPTY_KEYBOARD_REPORT,
});
&KB_REPORT
let hid_state = {
static HID_STATE: StaticCell<hid::State<'static>> = 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,13 +117,27 @@ 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<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 mut button_pub = button_events.publisher().unwrap();
let mut button_sub = button_events.subscriber().unwrap();
select(
async {
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>, button::Event>,
) -> ! {
loop {
let WaitResult::Message(event) = button_sub.next_message().await else {
error!("lagged");
@ -124,7 +145,7 @@ async fn event_listener_task(mut events: KbEvents, reports: &'static Mutex<CS, R
};
loop {
let mut r = reports.lock().await;
let mut r = ctx.reports.lock().await;
match event {
button::Event::PressKey(k) => {
r.actual.press_key(k);
@ -138,50 +159,48 @@ async fn event_listener_task(mut events: KbEvents, reports: &'static Mutex<CS, R
// 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);
Timer::after(Duration::from_millis(1)).await;
ctx.report_signal.wait().await;
continue;
}
button::Event::ReleaseMod(m) if r.unsent.modifier_pressed(m) => {
ctx.report_signal.reset();
drop(r);
Timer::after(Duration::from_millis(1)).await;
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;
}
}
},
keypress_handler(&mut *events.subscriber, &mut *button_pub),
)
.await;
}
#[embassy_executor::task]
async fn report_task(
stream: HidStream,
handler: &'static Handler,
reports: &'static Mutex<CS, Reports>,
) {
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<CS, Reports>,
) -> 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
};

View File

@ -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),