Contributing
ANY contributions are welcome! There is a simple step by step guide for developers:
-
Before you start, you may want to read the [under the hood] section to understand how RMK works. Github Issue is also a good place for questions.
-
Checkout the active PRs, make sure that what you want to add isn't implemented by others.
-
Write your code!
-
Open a PR merging your code to main repo, make sure all CIs pass.
Under the hood
If you're not familiar with RMK, the following is a simple introduction of source code of RMK.
Project architecture
There're three crates in RMK project, rmk
, rmk-config
and rmk-macro
.
rmk-config
crate is the dependency of both rmk
and rmk-macro
, it includes both toml configs used in keyboard.toml
and normal config used in RMK core. rmk-macro
is a proc-macro helper of RMK, it reads keyboard.toml
config file and converts toml config to RMK config, generates the boilerplate code. The core of RMK stays in rmk
crate.
So, if you want to contribute new features of RMK, just look into rmk
core crate. If you want to add new chip support, rmk
and rmk-macro
should be updated, so that users could use keyboard.toml
to config keyboard with your new chip. And, if you want to add new configurations, look into rmk-config
.
RMK core
rmk
crate is the main crate, it provides several entry API to start the keyboard firmware. All the entry APIs are similar, it:
- Initialize the storage and keymap first
- Create services: main keyboard service, usb service, ble service, vial service, light service, etc.
- Create
keyboard_report_channel
which is used to send message from matrix scanner to communication task(USB or BLE) - Run all tasks in an infinite loop, if there's a task failed, wait some time and rerun
Generally, there are 4-5 running tasks in the meanwhile, according to the user's config. Communication between tasks is done by channel. There are two common channel: FLASH_CHANNEL
and keyboard_report_channel
.
The former is global, which is actually a multi-sender, single-receiver channel. There are many tasks send the FlashOperationMessage
, such as BLE task(which saves bond info), vial task(which saves key), etc.
keyboard_report_channel
is a SPSC channel: keyboard task sends keyboard report to channel when scanning the matrix, and USB/BLE task receives the keyboard report and sends the key to the host.
Matrix scanning
An important part of a keyboard firmware is how it performs matrix scanning and how it processes the scanning result to generate keys.
In RMK, this work is done in keyboard_task
. The keyboard_task
is very simple: it scans the matrix, saves scanning result in Keyboard
struct and sends keyboard reports to USB/BLE tasks after each scanning:
#![allow(unused)] fn main() { // Main loop of keybaord_task loop { let _ = keyboard.scan_matrix(sender).await; keyboard.send_keyboard_report(sender).await; keyboard.send_media_report(sender).await; keyboard.send_mouse_report(sender).await; keyboard.send_system_control_report(sender).await; } }