diff --git a/rmk-types/src/protocol/rynk/payload/system.rs b/rmk-types/src/protocol/rynk/payload/system.rs index 84e1d1579..51052c3c3 100644 --- a/rmk-types/src/protocol/rynk/payload/system.rs +++ b/rmk-types/src/protocol/rynk/payload/system.rs @@ -20,8 +20,8 @@ pub struct ProtocolVersion { impl ProtocolVersion { /// Current protocol version for this firmware release. - /// Now the protocol is still being developed, so the version is v0.1 - pub const CURRENT: Self = Self { major: 0, minor: 1 }; + /// Now the protocol is still being developed, so the version is v0.2 + pub const CURRENT: Self = Self { major: 0, minor: 2 }; } /// Device capabilities discovered during the connection handshake. @@ -151,8 +151,8 @@ pub enum StorageResetMode { #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] #[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))] pub struct BehaviorConfig { - pub combo_timeout_ms: u16, - pub oneshot_timeout_ms: u16, + pub combo_timeout_ms: u32, + pub oneshot_timeout_ms: u32, pub tap_interval_ms: u16, pub tap_capslock_interval_ms: u16, } @@ -283,8 +283,8 @@ mod tests { #[test] fn round_trip_behavior_config() { round_trip(&BehaviorConfig { - combo_timeout_ms: 50, - oneshot_timeout_ms: 500, + combo_timeout_ms: 120_000, + oneshot_timeout_ms: 300_000, tap_interval_ms: 200, tap_capslock_interval_ms: 20, }); diff --git a/rmk-types/src/protocol/rynk/snapshots/wire_frames.snap b/rmk-types/src/protocol/rynk/snapshots/wire_frames.snap index acda024a3..a06912194 100644 --- a/rmk-types/src/protocol/rynk/snapshots/wire_frames.snap +++ b/rmk-types/src/protocol/rynk/snapshots/wire_frames.snap @@ -10,7 +10,7 @@ BootloaderJump reply Ok(()) 04 00 01 01 00 00 BootloaderJump request () 04 00 01 00 00 ConnectionChange topic ConnectionStatus{Configured,{1,Adv},Ble} 03 80 00 04 00 02 01 00 01 -GetBehaviorConfig reply Ok(BehaviorConfig{50,500,200,20}) 01 06 01 07 00 00 32 f4 03 c8 01 14 +GetBehaviorConfig reply Ok(BehaviorConfig{120000,300000,200,20}) 01 06 01 0a 00 00 c0 a9 07 e0 a7 12 c8 01 14 GetBehaviorConfig request () 01 06 01 00 00 GetCapabilities reply Ok(DeviceCapabilities{1..16}) 02 00 01 16 00 00 01 02 03 04 05 06 07 08 09 0a 01 00 01 0b 00 0c 0d 0e 0f 10 01 GetCapabilities request () 02 00 01 00 00 @@ -44,7 +44,7 @@ GetMorse reply Ok(Morse{TAP->Key(A)}) 01 04 01 GetMorse request 0 01 04 01 01 00 00 GetSleepState reply Ok(true) 06 08 01 02 00 00 01 GetSleepState request () 06 08 01 00 00 -GetVersion reply Ok(CURRENT) 01 00 01 03 00 00 00 01 +GetVersion reply Ok(CURRENT) 01 00 01 03 00 00 00 02 GetVersion request () 01 00 01 00 00 GetWpm reply Ok(42) 05 08 01 02 00 00 2a GetWpm request () 05 08 01 00 00 @@ -55,7 +55,7 @@ Lock request () 08 00 01 Reboot reply Ok(()) 03 00 01 01 00 00 Reboot request () 03 00 01 00 00 SetBehaviorConfig reply Ok(()) 02 06 01 01 00 00 -SetBehaviorConfig request BehaviorConfig{50,500,200,20} 02 06 01 06 00 32 f4 03 c8 01 14 +SetBehaviorConfig request BehaviorConfig{120000,300000,200,20} 02 06 01 09 00 c0 a9 07 e0 a7 12 c8 01 14 SetCombo reply Ok(()) 02 03 01 01 00 00 SetCombo request SetComboRequest{3,combo} 02 03 01 0a 00 03 01 02 01 00 04 05 01 01 02 SetDefaultLayer reply Ok(()) 04 01 01 01 00 00 diff --git a/rmk-types/src/protocol/rynk/snapshots/wire_values.snap b/rmk-types/src/protocol/rynk/snapshots/wire_values.snap index 2878a8e3f..476d48894 100644 --- a/rmk-types/src/protocol/rynk/snapshots/wire_values.snap +++ b/rmk-types/src/protocol/rynk/snapshots/wire_values.snap @@ -28,7 +28,7 @@ Action::TriggerMacro(7) 0c 07 Action::User(9) 13 09 BatteryStatus::Available{Discharging,85} 01 01 01 55 BatteryStatus::Unavailable 00 -BehaviorConfig{50,500,200,20} 32 f4 03 c8 01 14 +BehaviorConfig{120000,300000,200,20} c0 a9 07 e0 a7 12 c8 01 14 BleState::Advertising 00 BleState::Connected 01 BleState::Inactive 02 @@ -64,7 +64,7 @@ ModifierCombination(LCtrl|RGui) 81 MorseProfile(Normal,200,150) c8 81 b0 89 0c Morse{TAP->Key(A)} 00 01 02 01 00 04 MouseButtons(B1|B8) 81 -ProtocolVersion::CURRENT 00 01 +ProtocolVersion::CURRENT 00 02 ProtocolVersion{1,0} 01 00 Result<(),RynkError>::Err(StorageFault) 01 02 Result<(),RynkError>::Ok 00 diff --git a/rmk-types/src/protocol/rynk/tests.rs b/rmk-types/src/protocol/rynk/tests.rs index 201f4bcfb..6f7be10c2 100644 --- a/rmk-types/src/protocol/rynk/tests.rs +++ b/rmk-types/src/protocol/rynk/tests.rs @@ -252,8 +252,8 @@ fn exemplars() -> Exemplars { serial_number: heapless::String::try_from("rynk:0001").unwrap(), }; let behavior = BehaviorConfig { - combo_timeout_ms: 50, - oneshot_timeout_ms: 500, + combo_timeout_ms: 120_000, + oneshot_timeout_ms: 300_000, tap_interval_ms: 200, tap_capslock_interval_ms: 20, }; @@ -459,7 +459,7 @@ fn wire_values_locked() { ("MatrixState{[0x05,0x00,0x20]}", encode(&ex.matrix)), ("DeviceCapabilities{1..16}", encode(&ex.capabilities)), ("DeviceInfo{1.2.3,4,5,RMK,..}", encode(&ex.device_info)), - ("BehaviorConfig{50,500,200,20}", encode(&ex.behavior)), + ("BehaviorConfig{120000,300000,200,20}", encode(&ex.behavior)), ("ConnectionStatus{Configured,{1,Adv},Ble}", encode(&ex.connection)), ("ProtocolVersion{1,0}", encode(&ProtocolVersion { major: 1, minor: 0 })), ("ProtocolVersion::CURRENT", encode(&ProtocolVersion::CURRENT)), @@ -831,7 +831,7 @@ fn wire_frames_locked() { encode_frame(Cmd::GetBehaviorConfig, SEQ, &()) ), ( - "GetBehaviorConfig reply Ok(BehaviorConfig{50,500,200,20})", + "GetBehaviorConfig reply Ok(BehaviorConfig{120000,300000,200,20})", encode_frame( Cmd::GetBehaviorConfig, SEQ, @@ -839,7 +839,7 @@ fn wire_frames_locked() { ), ), ( - "SetBehaviorConfig request BehaviorConfig{50,500,200,20}", + "SetBehaviorConfig request BehaviorConfig{120000,300000,200,20}", encode_frame(Cmd::SetBehaviorConfig, SEQ, &ex.behavior) ), ( diff --git a/rmk/src/host/context.rs b/rmk/src/host/context.rs index e13988c2f..a120c1187 100644 --- a/rmk/src/host/context.rs +++ b/rmk/src/host/context.rs @@ -241,13 +241,13 @@ impl<'a> KeyboardContext<'a> { self.keymap.morse_prior_idle_time() } - pub async fn set_combo_timeout(&self, ms: u16) { + pub async fn set_combo_timeout(&self, ms: u32) { self.keymap.set_combo_timeout(Duration::from_millis(ms as u64)); #[cfg(feature = "storage")] FLASH_CHANNEL.send(FlashOperationMessage::ComboTimeout(ms)).await; } - pub async fn set_one_shot_timeout(&self, ms: u16) { + pub async fn set_one_shot_timeout(&self, ms: u32) { self.keymap.set_one_shot_timeout(Duration::from_millis(ms as u64)); #[cfg(feature = "storage")] FLASH_CHANNEL.send(FlashOperationMessage::OneShotTimeout(ms)).await; diff --git a/rmk/src/host/rynk/handlers/behavior.rs b/rmk/src/host/rynk/handlers/behavior.rs index 25ef646ed..edc8f0a8f 100644 --- a/rmk/src/host/rynk/handlers/behavior.rs +++ b/rmk/src/host/rynk/handlers/behavior.rs @@ -9,8 +9,8 @@ use super::Handle; impl Handle for RynkService<'_> { async fn handle(&self, _: ()) -> Result { Ok(BehaviorConfig { - combo_timeout_ms: self.ctx.combo_timeout().as_millis() as u16, - oneshot_timeout_ms: self.ctx.one_shot_timeout().as_millis() as u16, + combo_timeout_ms: u32::try_from(self.ctx.combo_timeout().as_millis()).unwrap_or(u32::MAX), + oneshot_timeout_ms: u32::try_from(self.ctx.one_shot_timeout().as_millis()).unwrap_or(u32::MAX), tap_interval_ms: self.ctx.tap_interval(), tap_capslock_interval_ms: self.ctx.tap_capslock_interval(), }) diff --git a/rmk/src/host/via/vial.rs b/rmk/src/host/via/vial.rs index 6a0bd6619..993c32497 100644 --- a/rmk/src/host/via/vial.rs +++ b/rmk/src/host/via/vial.rs @@ -117,7 +117,7 @@ pub(crate) async fn process_vial<'a>( match value.into() { SettingKey::None => report.input_data[0] = 0xFF, SettingKey::ComboTimeout => { - let combo_timeout = ctx.combo_timeout().as_millis() as u16; + let combo_timeout = u16::try_from(ctx.combo_timeout().as_millis()).unwrap_or(u16::MAX); LittleEndian::write_u16(&mut report.input_data[1..3], combo_timeout); } SettingKey::MorseTimeout => { @@ -125,7 +125,7 @@ pub(crate) async fn process_vial<'a>( LittleEndian::write_u16(&mut report.input_data[1..3], tapping_term); } SettingKey::OneShotTimeout => { - let one_shot_timeout = ctx.one_shot_timeout().as_millis() as u16; + let one_shot_timeout = u16::try_from(ctx.one_shot_timeout().as_millis()).unwrap_or(u16::MAX); LittleEndian::write_u16(&mut report.input_data[1..3], one_shot_timeout); } SettingKey::TapInterval => { @@ -174,7 +174,7 @@ pub(crate) async fn process_vial<'a>( SettingKey::None => (), SettingKey::ComboTimeout => { let combo_timeout = u16::from_le_bytes([report.output_data[4], report.output_data[5]]); - ctx.set_combo_timeout(combo_timeout).await; + ctx.set_combo_timeout(combo_timeout.into()).await; } SettingKey::MorseTimeout => { let timeout_time = u16::from_le_bytes([report.output_data[4], report.output_data[5]]); @@ -183,7 +183,7 @@ pub(crate) async fn process_vial<'a>( } SettingKey::OneShotTimeout => { let timeout_time = u16::from_le_bytes([report.output_data[4], report.output_data[5]]); - ctx.set_one_shot_timeout(timeout_time).await; + ctx.set_one_shot_timeout(timeout_time.into()).await; } SettingKey::TapInterval => { let tap_interval = u16::from_le_bytes([report.output_data[4], report.output_data[5]]); diff --git a/rmk/src/storage/mod.rs b/rmk/src/storage/mod.rs index fa2e669d3..a15e7274d 100644 --- a/rmk/src/storage/mod.rs +++ b/rmk/src/storage/mod.rs @@ -140,9 +140,9 @@ pub(crate) enum FlashOperationMessage { // Current saved connection type ConnectionType(ConnectionType), // Timeout time for combos - ComboTimeout(u16), + ComboTimeout(u32), // Timeout time for one-shot keys - OneShotTimeout(u16), + OneShotTimeout(u32), // Interval for tap actions TapInterval(u16), // Interval for tapping capslock @@ -305,9 +305,9 @@ pub(crate) struct BehaviorConfig { pub(crate) morse_default_profile: MorseProfile, // Timeout time for combos - pub(crate) combo_timeout: u16, + pub(crate) combo_timeout: u32, // Timeout time for one-shot keys - pub(crate) one_shot_timeout: u16, + pub(crate) one_shot_timeout: u32, // Interval for tap actions pub(crate) tap_interval: u16, // Interval for tapping capslock. @@ -333,8 +333,8 @@ impl From<&config::BehaviorConfig> for StorageData { Self::BehaviorConfig(BehaviorConfig { prior_idle_time: behavior.morse.prior_idle_time.as_millis() as u16, morse_default_profile: behavior.morse.default_profile, - combo_timeout: behavior.combo.timeout.as_millis() as u16, - one_shot_timeout: behavior.one_shot.timeout.as_millis() as u16, + combo_timeout: u32::try_from(behavior.combo.timeout.as_millis()).unwrap_or(u32::MAX), + one_shot_timeout: u32::try_from(behavior.one_shot.timeout.as_millis()).unwrap_or(u32::MAX), tap_interval: behavior.tap.tap_interval, tap_capslock_interval: behavior.tap.tap_capslock_interval, }) @@ -1007,6 +1007,47 @@ mod tests { } } + #[test] + fn behavior_storage_preserves_long_timeouts() { + let mut behavior = RuntimeBehaviorConfig::default(); + behavior.combo.timeout = Duration::from_secs(120); + behavior.one_shot.timeout = Duration::from_secs(300); + + let StorageData::BehaviorConfig(stored) = StorageData::from(&behavior) else { + panic!("expected behavior storage data"); + }; + assert_eq!(stored.combo_timeout, 120_000); + assert_eq!(stored.one_shot_timeout, 300_000); + } + + #[test] + fn behavior_storage_reads_legacy_u16_timeouts() { + #[derive(serde::Serialize)] + struct LegacyBehaviorConfig { + prior_idle_time: u16, + morse_default_profile: MorseProfile, + combo_timeout: u16, + one_shot_timeout: u16, + tap_interval: u16, + tap_capslock_interval: u16, + } + + let legacy = LegacyBehaviorConfig { + prior_idle_time: 100, + morse_default_profile: MorseProfile::default(), + combo_timeout: 50, + one_shot_timeout: 500, + tap_interval: 200, + tap_capslock_interval: 20, + }; + let mut buffer = [0u8; 64]; + let encoded = postcard::to_slice(&legacy, &mut buffer).unwrap(); + let decoded: BehaviorConfig = postcard::from_bytes(encoded).unwrap(); + + assert_eq!(decoded.combo_timeout, 50); + assert_eq!(decoded.one_shot_timeout, 500); + } + #[test] fn build_hash_mismatch_reinitializes_storage() { block_on(async { diff --git a/rmk/tests/rynk_loopback.rs b/rmk/tests/rynk_loopback.rs index c9c02fdc5..983a14444 100644 --- a/rmk/tests/rynk_loopback.rs +++ b/rmk/tests/rynk_loopback.rs @@ -1030,8 +1030,8 @@ fn get_set_behavior_config_round_trip() { // Non-default values make a dropped Set observable. link_session(&service, async |client| { let cfg = WireBehaviorConfig { - combo_timeout_ms: 123, - oneshot_timeout_ms: 456, + combo_timeout_ms: 120_000, + oneshot_timeout_ms: 300_000, tap_interval_ms: 78, tap_capslock_interval_ms: 90, }; diff --git a/rynk/rynk-wasm/index.html b/rynk/rynk-wasm/index.html index 41524fd6e..991264b79 100644 --- a/rynk/rynk-wasm/index.html +++ b/rynk/rynk-wasm/index.html @@ -244,7 +244,7 @@

Rynk web demo

// Keep the loader keyed by protocol major even while there is one wasm artifact. async function loadCore(major) { switch (major) { - case 0: // protocol v0.x (ProtocolVersion::CURRENT = {0, 1}) + case 0: // protocol v0.x (ProtocolVersion::CURRENT = {0, 2}) case 1: return await import("./pkg/rynk_wasm.js"); default: