rmk icon indicating copy to clipboard operation
rmk copied to clipboard

Tracking issue for input device support

Open HaoboGu opened this issue 1 year ago • 9 comments

RMK should support other input devices, like touchpad, encoder, joystick, etc.

roadmap:

  • [x] #192
  • [x] #229
  • [x] #230
  • [ ] #231
  • [ ] #232
  • [ ] #233

HaoboGu avatar Dec 13 '24 07:12 HaoboGu

Related issue: #29 #69

HaoboGu avatar Dec 13 '24 07:12 HaoboGu

Since the generic task limitation(https://github.com/embassy-rs/embassy/issues/2454), the only way for defining a generic input device task is to use macro. There are two options:

  1. Use a proc-macro like:
#[input_device]
struct RotaryEncoder {}
impl InputDevice for RotaryEncoder {...}

which could be expanded as:

struct RotaryEncoder {}
impl InputDevice for RotaryEncoder {...}
#[embassy_executor::task]
async fn rotaryencoder_task(device: RotaryEncoder) -> ! {
  device.run().await
}
  1. Use a decl macro
struct RotaryEncoder {}
impl InputDevice for RotaryEncoder {...}
impl_input_device!(RotaryEncoder, task_name);

expanded code:

struct RotaryEncoder {}
impl InputDevice for RotaryEncoder {...}
#[embassy_executor::task]
async fn task_name(device: RotaryEncoder) -> ! {
  device.run().await
}
  • option 1: simple and easy to use, but it's hard to make the task name customizable; better diagnosis if users misuse it.
  • option 2: the task name can be customized, but need calling impl_input_device for each device; might be hard to debug if users misuse it

Now I'm going to option 2 first, Any ideas are welcome:)

HaoboGu avatar Dec 24 '24 06:12 HaoboGu

Okay, after some testing I found neither work with generic tasks. So I have to give up embassy task. The only option seems to be join all input device's run together:

// Implement the input device for RotaryEncoder
struct RotaryEncoder {}
impl InputDevice for RotaryEncoder {...}

in user space:

let e1 = RotaryEncoder {};
let e2 = RotaryEncoder {};
join(run_rmk(), run_devices!(e1, e2)).await

HaoboGu avatar Dec 24 '24 08:12 HaoboGu

I have an idea to make the input device/processor more flexible and avoid the unstable feature #[feature(generic_const_exprs)]

https://github.com/drindr/rmk/blob/3710603d4df71b5f27456617e48c41994b79effd/rmk/src/input_device/mod.rs#L103-L143

drindr avatar Jan 28 '25 06:01 drindr

I have an idea to make the input device/processor more flexible and avoid the unstable feature #[feature(generic_const_exprs)]

https://github.com/drindr/rmk/blob/3710603d4df71b5f27456617e48c41994b79effd/rmk/src/input_device/mod.rs#L103-L143

The main difference is to use a trait for receiver/sender instead of channels, am I right?

BTW in #250 I also changed a little bit on input processors

HaoboGu avatar Jan 28 '25 14:01 HaoboGu

I'm thinking about removing receiver/sender, adding something like DeviceBuilder to bind the input device and corresponding processors. In current implementation, all the input device and processors are linked separately by channels, it's not easy for end users to configure them

HaoboGu avatar Feb 26 '25 02:02 HaoboGu

End with something like:

run_devices!{
  (matrix, encoder, touchpad) => EVENT_CHANNEL
}

run_processor_chain! {
  EVENT_CHANNEL => [touchpad_processor, encoder_processor]
}

The channel can be customized, but I'm not sure that should we make channel customized? or just use built-in channels for all devices

HaoboGu avatar Mar 13 '25 15:03 HaoboGu

End with something like:

run_devices!{ (matrix, encoder, touchpad) => EVENT_CHANNEL }

run_processor_chain! { EVENT_CHANNEL => [touchpad_processor, encoder_processor] }

The channel can be customized, but I'm not sure that should we make channel customized? or just use built-in channels for all devices

I think it would be useful when a local processor is required, especially for split. For instance,

  1. the boot/bootloader switch on the peripheral should be processed on the itself. (a small keymap should be placed in the peripheral. Or just triggered by the master?)
  2. the peripheral changes the master. (I'm not sure if someone have such a requirement)

drindr avatar Mar 13 '25 16:03 drindr

End with something like: run_devices!{ (matrix, encoder, touchpad) => EVENT_CHANNEL } run_processor_chain! { EVENT_CHANNEL => [touchpad_processor, encoder_processor] } The channel can be customized, but I'm not sure that should we make channel customized? or just use built-in channels for all devices

I think it would be useful when a local processor is required, especially for split. For instance,

  1. the boot/bootloader switch on the peripheral should be processed on the itself. (a small keymap should be placed in the peripheral. Or just triggered by the master?)
  2. the peripheral changes the master. (I'm not sure if someone have such a requirement)

It runs on the peripheral side, we could have it as a proxy/filter of peripheral events, before the peripheral sends events out. Luckily it has nothing conflict with current implementation:D

HaoboGu avatar Mar 13 '25 16:03 HaoboGu