Add naive Compose button impl
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1532,7 +1532,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tgnt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.nubo.sh/hulthe/tgnt.git#c34c317f4e189c9937ea6da1342272efd86ad88d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
[patch.'https://git.nubo.sh/hulthe/tgnt.git'.tgnt]
|
||||
path = "../tgnt"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"lib",
|
||||
|
||||
@ -49,6 +49,7 @@ pub mod button {
|
||||
ReleaseKey(Key),
|
||||
PressMod(Modifier),
|
||||
ReleaseMod(Modifier),
|
||||
Wait,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,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<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 {
|
||||
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>, 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<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
|
||||
};
|
||||
|
||||
@ -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),
|
||||
|
||||
Reference in New Issue
Block a user