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