Display Graphics & Fonts
Each of the 72 per-key OLED displays is a 72×40 pixel monochrome canvas. Rendering uses a fork of the Adafruit GFX library, adapted for the RP2040 and the FPC-connected display array.
- Display rendering / font tooling repo: github.com/thpoll83/adafruit-gfx-library
- Display driver: integrated into
keyboards/handwired/polykybd/(base/disp_array.c,poly_keymap.c) in the QMK firmware fork
The rendering pipeline
- The firmware maintains the keycap image data in overlay memory (
base/overlay.c— 90 keycap slots × 9 modifier variants × 360 bytes each). - The Adafruit GFX fork provides the drawing primitives used to compose text and bitmaps into the per-keycap framebuffer.
- On a key or state change,
base/disp_array.cselects the target keycap OLED (via shift-register multiplexing) and writes the bitmap over SPI;kdisp_invert()gives instant pressed-key feedback. - PolyKybdHost pushes per-key overlay content over HID using 64-byte reports (protocol v0.7.0+). Overlays are sent RLE-compressed and decompressed in the firmware — optionally offloaded to RP2040 core1 (
multicore_exec.c) to keep QMK’s main loop responsive. See the HID Protocol Reference for the overlay commands.
An overlay is 360 bytes per keycap, and the overlay index is keycode_slot + 90 * modifier_variant (9 modifier variants: bare, Ctrl, Shift, Ctrl+Shift, Alt, Ctrl+Alt, Alt+Shift, Ctrl+Alt+Shift, GUI).
Font generation pipeline
Keycap fonts are not hand-rolled C arrays. They are generated by a config-driven pipeline that drives the fontconvert tool to turn TTF/OTF fonts into Adafruit GFX .h bitmap headers.
The pipeline lives in the firmware repo under keyboards/handwired/polykybd/fonts/:
| File | Role |
|---|---|
fonts/fonts.yaml | Single source of truth — an ordered list of font entries (font file, size, variant, codepoint ranges, weight, bit depth, …) grouped into categories with shared defaults. The list order is the lookup priority (first match wins on overlapping ranges). |
fonts/generate_fonts.py | Reads the YAML, runs fontconvert once per entry, writes one header per category to base/fonts/generated/, and composes base/fonts/gfx_used_fonts.h (the ALL_FONTS[] table). --check flags stale headers for CI. |
fonts/dl-fonts.sh | Downloads the Noto source fonts. |
fonts/gen-lang-fonts.sh | Generates the standalone headers for the language-selection layer — country flag glyphs and the small label font. |
To regenerate after editing fonts.yaml (needs PyYAML and fontconvert on PATH, or $FONTCONVERT):
cd keyboards/handwired/polykybd/fonts./dl-fonts.sh # one-time: fetch Noto source fontspython generate_fonts.py # regenerate headers into base/fonts/generated/python generate_fonts.py --check # CI: verify headers are up to dateThe fontconvert tool
fontconvert is a standalone C tool in the Adafruit-GFX repo (fontconvert/). It converts a TTF/OTF font into an Adafruit GFX GFXfont header, rendering each glyph (FreeType, with HarfBuzz shaping for multi-codepoint emoji/flag sequences) and dithering it to a 1-bit bitmap. The firmware-side pipeline calls it once per fonts.yaml entry — fontconvert itself does not read the config; it stays a focused single-font converter. See the repo’s fontconvert/README.md for build and usage details.