Shows the active layer of a Ferris Sweep running QMK as a coloured icon in the GNOME top bar, and can show what every key does on that layer in a small live window.
Keyboard layers only exist inside the firmware: the computer just receives the final keys. This project adds a tiny piece of firmware that tells the computer which layer is active, and a small desktop app that turns that into a tray icon. No need to open VIA to remember what is on each layer.
Ferris Sweep (QMK + VIA) Ubuntu / GNOME
┌─────────────────────────┐ Raw HID ┌──────────────────────────────┐
│ layer_state_set_user() │ ── "LY" + n ─▶ │ ferris-layer (systemd --user)│
│ via_command_kb() │ ◀─ "LY" ────── │ reads /dev/hidrawN │
└─────────────────────────┘ │ updates the top bar icon │
└──────────────────────────────┘
- On every layer change the firmware sends a 32-byte Raw HID report:
'L' 'Y' <layer>. - When the app connects it sends
'L' 'Y'and the firmware answers with the current layer, so the icon is right from the start. - VIA keeps working: the reports share the VIA Raw HID channel and use a prefix that no VIA command uses.
- If the keyboard is unplugged the app waits and reconnects on its own.
- Ubuntu (or another Debian-based distro) with GNOME. Tested on Ubuntu 24.04 on X11.
- A Ferris Sweep with QMK. The build script defaults to a Pro Micro RP2040 and supports any QMK converter (
elite_pi,kb2040,sparkfun_pm2040…). - uv and Docker (to build the firmware).
Everything goes through make. Run make alone to list the commands:
| Command | What it does |
|---|---|
make install |
Install the top bar app (asks for sudo). |
make backup |
Save the keyboard's VIA keymap and macros. |
make restore |
Write the backup back to the keyboard. |
make keymap |
Generate the QMK keymap.c from the backup. |
make images |
Draw every layer of the backup as SVG images. |
make firmware |
Build the firmware with Docker. |
make flash |
Wait for a half in bootloader mode and copy the firmware to it. |
make test |
Run the tests. |
make run |
Run the top bar app from the source tree. |
They work on the author's keymap by default. Pass KEYMAP=myname to use firmware/keymaps/myname/ and its via-backup.json, CONVERTER=elite_pi for another controller, BACKUP=path.json for a backup somewhere else and IMAGES=folder to draw the images somewhere else.
git clone https://github.com/quiqueporta/ferris-layer.git
cd ferris-layer
make installmake install runs install.sh, which:
- Installs the system packages it needs:
python3-gi,gir1.2-gtk-3.0,gir1.2-ayatanaappindicator3-0.1,gnome-shell-extension-appindicatorandlibrsvg2-common(to draw the layout window). - Creates a virtualenv with uv on the system Python with
--system-site-packages. GTK bindings are much easier to get from apt than to build from PyPI. - Installs a udev rule so your user can read the keyboard's
/dev/hidrawdevice without root. - Copies
config.example.tomlto~/.config/ferris-layer/config.toml. - Installs and starts the
ferris-layersystemd user service.
On Ubuntu the AppIndicator extension is enabled by default. On other GNOME distros enable it with gnome-extensions enable appindicatorsupport@rgcjonas.gmail.com.
Choose Show layout in the icon's menu to open a small window that stays on top and draws the active layer, with the same labels as the layer images. It follows every layer change while it is open.
Each time it opens it reads the keymap and macros straight from the keyboard, so it always shows what you have set in VIA. Closing it just hides it.
The icon menu shows the layer name. Edit ~/.config/ferris-layer/config.toml:
[layers]
0 = "Letters"
1 = "Symbols"
2 = "Numbers"
3 = "Functions"Layers without a name are shown as Layer N. Restart the service after editing: systemctl --user restart ferris-layer.
Every command takes KEYMAP=myname to work on your own keymap folder, firmware/keymaps/myname/. The backup lives in that folder as via-backup.json.
Warning
Never plug or unplug the TRRS cable between the halves while the USB cable is connected. It can short and damage the controller. Always unplug USB first, and plug it back last.
Flashing a new VIA firmware usually wipes the keymap and macros stored in the keyboard's EEPROM. With the keyboard plugged in as usual, save them first:
mkdir -p firmware/keymaps/myname
make backup KEYMAP=mynameGenerate a QMK keymap from the backup. It boots with exactly your current layout, and the layer report is already wired in:
cp firmware/keymaps/quique/rules.mk firmware/keymaps/myname/
make keymap KEYMAP=mynameIf you already have your own QMK keymap, add the files from firmware/layer_report/ to it and add these lines:
#include "layer_report.h"
layer_state_t layer_state_set_user(layer_state_t state) {
layer_report_announce(state);
return state;
}
bool via_command_kb(uint8_t *data, uint8_t length) {
return layer_report_answer(data);
}# rules.mk
VIA_ENABLE = yes
SRC += layer_report.cClone QMK once, then build inside the official QMK Docker image:
git clone --depth 1 --recurse-submodules --shallow-submodules https://github.com/qmk/qmk_firmware.git ~/qmk_firmware
make firmware KEYMAP=mynameThe firmware ends up in firmware/build/. Add CONVERTER=elite_pi (or your controller) if it is not a Pro Micro RP2040, and set QMK_HOME if QMK lives somewhere else.
Both halves need the same firmware, so they keep understanding each other over the TRRS cable. Flash them one at a time, each with its own USB connection.
Left half
- Run
make flash. It waits up to two minutes for the half to show up. - Unplug the USB cable from the keyboard.
- Only then, unplug the TRRS cable between the halves.
- Hold
Q(the outer top key of the left half) and, while holding it, plug the USB cable into the left half. This is QMK bootmagic: instead of starting as a keyboard, the half shows up as a drive calledRPI-RP2. make flashcopies the firmware to it. The half reboots by itself and the drive disappears. Do not unplug anything untilmake flashsays it is done.- Unplug the USB cable.
Right half
- Run
make flashagain. - Hold
P(the outer top key of the right half) and, while holding it, plug the USB cable directly into the right half. - Wait until
make flashsays it is done, then unplug the USB cable.
Put it back together
- With USB still unplugged, connect the TRRS cable between the halves.
- Plug the USB cable into the left half, without holding any key.
- Type on both halves to check that they work.
If the RPI-RP2 drive does not appear, the firmware you have now may not have bootmagic. Use a QK_BOOT key instead: assign it in VIA to a key on some layer, plug in the half as usual and press it. Some boards also have a reset button soldered on the PCB that does the same.
Prefer to copy the file yourself? Copy the .uf2 from firmware/build/ to the RPI-RP2 drive with cp. GNOME Files sometimes shows the drive as read-only even though it is not.
The keymap is already in the firmware, but macros only live in the backup. With the keyboard plugged in as usual:
make restore KEYMAP=mynameYou only need QMK to build and flash the firmware. Day-to-day changes to keys and macros are still made in VIA, with no rebuild.
-
keymap.cis only the starting layout: the one the keyboard has right after flashing or after its EEPROM is reset. Whatever you change in VIA is stored in the EEPROM and takes precedence over it. -
After significant changes in VIA, save a fresh backup so nothing is lost the next time you flash:
make backup KEYMAP=myname
Draw every layer of a VIA backup as an SVG image, with the Sweep's shape:
make images KEYMAP=myname IMAGES=docs/layersEach key shows what it types. Keys that do something else when held show it underneath (Alt, L1), and macros show what they do (Win+L, Ctrl+Alt+←) instead of M0. Labels assume a US layout, as VIA does.
Save a fresh backup first with make backup, so the images match what the keyboard has now.
firmware/keymaps/quique/ holds the author's keymap, and docs/layers/ its images, both generated from via-backup.json in the same folder.
| Layer | Name | |
|---|---|---|
| 0 | Letters | |
| 1 | Symbols (hold Space) |
|
| 2 | Numbers (TO(2)) |
|
| 3 | Functions (TO(3)) |
uv venv --python /usr/bin/python3 --system-site-packages
make test
make runThe tests also run on GitHub Actions on every push and pull request.
| Module | Role |
|---|---|
layer_watcher |
Turns keyboard reports into layers to display. |
layer_report |
Knows the LY report format. |
keyboard_connection |
Finds the keyboard, asks for its layer, reconnects when unplugged. |
hidraw_finder, hidraw_keyboard |
Talk to /dev/hidraw. |
tray_indicator_display, layer_icons |
Draw the top bar icon. |
displays |
Shows each layer change on the icon and on the layout window. |
live_layout, keyboard_keymap |
Decide what the layout window shows, reading the keymap from the keyboard. |
layout_window |
The GTK layout window. |
via_protocol, via_backup, keymap_source |
VIA backup, restore and keymap generation. |
via_keymap, via_macros |
Read the keymap and macros stored in a VIA backup. |
keycode_labels |
Turns QMK keycodes into keycap labels. |
backup_drawings, layer_images, layer_drawing, sweep_layout |
Draw each layer as an SVG image. |
ferris_sweep |
USB ids and matrix size of the Ferris Sweep. |
MIT. The firmware you build links against QMK, which is GPL-2.0-or-later.
