kodeOS internals
Buttons and gestures
What every button and every touch gesture does on every kodeOS screen, the buzzes the interface plays, and the recognizer that reads a touch.
What it is
The Kode Dot has six buttons. Four form the pad: up, down, left and right. Two round buttons sit to the right of it on the front: the blue TOP button and, lower, the red BOT button. Together they are the side buttons. This page lists what each button and each touch gesture does on each screen. Every row is an action or nothing (by design), which means the system chooses to do nothing for that input.
The short version:
- The face is home. A tap anywhere, TOP or the pad's right opens Apps. The pad's up, TOP held or a finger that drags down pulls Settings over it. BOT pressed and let go sends the face to Rest. The pad's down does nothing there.
- A pad button moves the cursor and acts when it goes down.
- TOP goes in or confirms and acts when you let go. Held for 0.6 s on an app, it opens the app's details. Held on the Wi-Fi or Bluetooth circle in Settings, it opens their page.
- BOT goes back and acts when you let go. In a folder it goes back to Apps' root, at the root to the face. On the face it sends the face to Rest. On Settings and on a page it goes back one level.
- Hold BOT for 0.6 s to go home, to Apps' root. A hold never locks: the face comes back when you go back from Apps' root, or by itself after the auto-lock time.
- On the face, hold BOT instead. From 2 s a red arc fills along the top, and at 5 s the card "Turn off?" rises.
- At Rest, TOP pressed twice wakes the face. The first press shows "Press ● again", where the blue dot is TOP and beats once a second. BOT and the pad show "Press ● twice". On a cable any button or touch wakes the face at once.
- A finger that moves more than 16 px is not a tap and acts on nothing.
- The interface buzzes lightly when something acts, refuses or arms. The Settings list sets how much.
How it is connected
flowchart LR btn["AO_Buttons"] -- "BUTTON_EVT_SIG<br/>BUTTON_HOLD_SIG" --> gui["AO_Gui"] boot["AO_Boot"] -- "HOME_SIG at 0.6 s" --> gui touch["AO_Touch"] -- "TOUCH_EVT_SIG" --> rec["recognizer<br/>kui_gesture"] rec -- "tap, claim, rejection" --> gui console["console: btn, tap, touch, swipe"] -. "injected" .-> gui gui -- "kode_haptic_play()" --> hap["AO_Haptic"] class btn ao class boot ao class touch ao class gui ao class hap ao class console ext
Where inputs come from
- AO_Buttons publishes each press, release and hold of the six buttons.
sdk/kaos/buttonshas the debounce and the hold. - AO_Boot turns a held BOT into
HOME_SIG. - AO_Touch publishes touch samples on change only. Every sample goes through the recognizer of the kit before LVGL reads it, and AO_Gui creates an LVGL input device in event mode so each sample triggers exactly one read.
gui_face_navigate(),gui_navigate(),gui_settings_navigate()andgui_sheet_navigate()inkgui_ao.cread the buttons of their screens.- The console's
btn,tap,touchandswipe(internals/command-set) drive the same inputs.
An injected press starts no hold, so btn id=bot state=press on the face begins the count of the red arc but never produces HOME_SIG, and btn id=bot state=hold is home alone.
A gesture only records an intent
LVGL calls a click callback inside its input processing, and a repaint there would delete objects the input device is still walking. A callback first ends its press with lv_indev_wait_release() where it must, then records an intent: a back (pending_back), a door, or a tap. The next tick applies it.
The arc counts edges, never the hold
On the face AO_Gui times BOT itself, from its press (bot_hold_ms, on khal_time_ms(), read by gui_off_arc_tick()). BUTTON_HOLD_SIG is not an edge and leaves the count running, although it disarms every armed press. From 2,000 ms (GUI_OFF_ARC_FROM_MS) the red arc fills, and at 5,000 ms (GUI_OFF_ARC_TO_MS) with BOT still down the card "Turn off?" rises. No hold on any screen locks the board.
5 s sits far past the 0.6 s home hold and 11 s below the shortest full reset the charger can make. BOT is the BQ25896's QON pin, and holding it for tQON_RST, 16 to 23 s, with no USB cuts and closes the battery path.
Specifications
Words
| Word | Meaning |
|---|---|
| Press, release | The edges of a button going down and coming up |
| Hold | A side button down for 0.6 s (KODE_BUTTON_HOLD_MS); BUTTON_HOLD_SIG |
| Armed | A side button whose press AO_Gui has seen: the action waits for the release |
| Swallow | Receive an input and do nothing with it |
| Home | Apps' root, asked for by HOME_SIG |
| Rest | The dimmed look of the face (internals/gui/face) |
| Tap | A short touch of one finger that never goes farther than 16 px from where it landed |
| Drag, swipe | The finger moves and an object follows it, or the recognizer reads a quick move in one direction |
| Flick | A quick drag; the list goes on after the finger lifts |
| Settings pull | A drag down on Apps from the top of the glass, or on the list when it rests at its top; Settings comes down over Apps with the finger |
| Folder | A group of apps on the rail, filed by their first category |
| Details | The page of one app, opened by TOP held or a finger held on its row |
| Top band | The first 60 rows of the glass (KGUI_TOP_BAND_PX), where the hint stands |
| Hint | A few words in the top band, for 2 s, that say why an input did nothing |
| Claim | The moment the recognizer decides a press is a drag |
| Curtain | A panel that comes down over a screen as a picture on a screen of its own |
| Busy sheet | The write screen and the Removing card, which answer no input. The log calls them locked |
| System card | A card the system raises. It ignores every input for 700 ms and takes no drag, except Turn off |
| Door | A request to change screen (internals/gui/overview) |
The face
gui_face_navigate() reads the buttons in Gui_awake, the awake look of Gui_face.
| Input | What happens |
|---|---|
| A tap anywhere, Kody and the top band included | Apps opens at its root |
| TOP, pressed and released | Apps opens at its root |
| The pad's right, pressed | Apps opens at its root |
| BOT, pressed and released before 2 s | The face goes to Rest |
| The pad's up, pressed | Settings comes down over the face |
| A drag down, from anywhere | Settings follows the finger down as a curtain |
| The pad's down, the pad's left | Nothing (by design) |
| TOP held | Settings comes down over the face. The release that follows does nothing |
| BOT held | Nothing at 0.6 s (HOME_SIG). From 2 s a red arc fills along the top |
| BOT held for 5 s | The arc is full and goes, and the card "Turn off?" rises |
| BOT let go between 2 s and 5 s | The arc empties in 160 ms. Nothing else happens |
| TOP and BOT together | Nothing. Two side buttons down arm neither |
| A swipe left | Apps opens at its root, sliding in from the right |
| A swipe right | Apps opens at its root, sliding in from the left |
| A swipe up | Nothing (by design). It is no tap either |
No input for the kody_sleep time | The face goes to Rest |
- A tap is the recognizer's (
gui_face_tap()): a press that travelled more than 16 px, or that the recognizer claimed, opens nothing. A tap on Kody does not make him look. - The press of a side button arms it and the release acts. The hold disarms the press it grew out of.
- BOT posts the door to Rest on the release, because the answer refuses while a button is held. The face goes to Rest only when it is bare: no card, no curtain, no finger, no held button and no write.
- The recognizer claims a drag down that starts anywhere on the face. A swipe left or right opens Apps, and a swipe up is a
zonerejection and no tap. gui_off_arc_tick()counts the hold from the press: the arc fills toward 5 s, the hold is spent with amarkbuzz when the card rises, and the hold disarms the press, so the release opens nothing.- Kody looks when the face comes back from Apps or from Settings, not on an input of the face.
- The scene of Kody plays on while the face is seen. When the
kody_sleeptime has gone by, the face goes to Rest. A finger on the screen, a held button, a sheet, a moving screen, a running write and a look of Kody hold the count.
The face at Rest
At Rest the face is dimmed. The state is Gui_rest. Nothing a person does reaches a screen from it. On the battery only TOP, pressed twice, wakes it. On a cable any button or touch wakes it at once.
| Input | What happens |
|---|---|
| TOP, pressed | The knock: the glass lights and "Press ● again" stands in the date's place. Its dot is Kody Blue, the colour of TOP, and beats once a second (KGUI_KNOCK_BEAT_HALF_MS, 495 ms each way). Left alone for 5.94 s (KGUI_PEEK_MS), the face rests again |
| TOP, pressed again during the knock | The face wakes on the press. The log says door: rest -> face (top pressed twice) |
| BOT or a pad button, pressed, on the battery | "Press ● twice" in the date's place, for 5.94 s. Nothing wakes |
| A touch on the battery: a tap, a swipe or a drag | Nothing, and nothing shows. The touch counts as blocked |
| Any button, on a cable | The face wakes on the press, and its release is swallowed. The log says door: rest -> face (button on a cable) |
| A touch on a cable | The face wakes on the press. The log says door: rest -> face (touch on a cable) |
| A system card: a pairing, a write, its failure, a critical battery | The card rises over the face and its arrival lights the screen |
| No input after a wake | The face rests again after 60 s (KGUI_WOKEN_REST_MS) |
Gui_rest records the waking button in wake_btn, and Gui_awake swallows the rest of that press (gui_wake_swallow()). Gui_rest takes every touch sample, so LVGL and the recognizer never see it, and the log writes no gesture: line. A touch that is down when the face wakes stays swallowed until the finger lifts.
Apps
The launcher is the rail of folders and apps (internals/gui/launcher). gui_navigate() reads the buttons, in Gui_browsing.
| Input | What happens |
|---|---|
| The pad's up or down | The selection moves one row and the rows roll under the focus line. No wrap; at an end the list gives a little and comes back. On an empty list, nothing |
| The pad's right | Goes in, as TOP does |
| The pad's left | Goes back: out of the open folder to the root, and from the root to the face |
| TOP, pressed and released | Goes in on the selected row: a folder opens, an installed app launches, a file is written and then launched. On an empty list, Kody's door works |
| TOP held | The details of the selected app open. A folder's row has none. The release that follows launches nothing |
| BOT, pressed and released | Goes back, as the pad's left does |
| BOT held | Home at 0.6 s: an open folder closes and Apps stays at its root. Nothing fills and nothing locks |
| A tap on a row | Goes in on the row, as TOP does. A tap on a row whose icon is not wholly inside the rail only moves the focus to it |
| A finger held on a row for 0.6 s | The details of an app open. A folder's row has none, and its lift is no tap either |
| A tap on Kody, on the empty launcher | Kody's door |
| A vertical drag or flick | The list follows the finger one to one and glides to the row the finger's speed reaches, at most five rows on. The row on the focus line is the selection |
| A press on a list that moves | The list stops where it is. The click of that press acts on nothing |
| A drag down from the top 44 rows of the glass, or on a list at rest at its top (the Settings pull) | Settings comes down over Apps with the finger. Letting go past halfway opens it, and closing it goes back to Apps |
| A swipe right | Goes back, as BOT does |
| A swipe left at the root | Goes to the face, which comes in from the right. In a folder, nothing (by design) |
| 20 s with no input, with the QR code showing | Kody comes back |
| No input for the auto-lock time (1 min by default) | The auto-lock: the face comes down over Apps |
- A pad button that stays down repeats: nothing for 400 ms (
KGUI_PAD_HOLD_MS), then one row every 115 ms, and after 8 repeats two rows each time. At an end the repeat stops. - Kody's door is the empty launcher's switch between Kody and the QR code. It changes no screen and does nothing while "Looking for apps" shows or Kody slides.
HOME_SIGcloses an open folder (gui_level_home()) and leaves Apps at its root. The release goes back nowhere, because the hold disarmed the press. The hintalready homeshows only for a hold that counts for nothing, such asbtn id=bot state=holdwith no press.- A swipe right is claimed 12 px from the landing; the level goes back when the finger lifts, if it went 40 px along the claim (
KGUI_SIDE_STEP_PX). A shorter slide is no tap and goes nowhere. It works on the list and on the empty box alike. - A drag down that starts in the top 44 rows (
KGUI_LIST_PULL_BAND) always pulls Settings, wherever the list stands. Lower down it pulls Settings only when the press landed on the list, the first row is the selection, the list is at rest there and nothing has moved it for 500 ms (gui_zone_lock_pull()). If one fails, the drag moves the list. - The auto-lock does not happen while a sheet is up, a finger rests, a button is held, a write runs or a screen moves.
A launch is one gesture. gui_launch_selection() has two callers: gui_level_enter() on an app row, and Run again on the Stopped card. TOP and a tap on an app row launch it at once, with no card between. Remove sits behind the details and behind a question of its own, so the one act that cannot be undone asks first.
The settings screen
The control centre. gui_settings_navigate() reads the buttons, in Gui_settings.
| Input | What happens |
|---|---|
| The pad's left or right | The ring walks the top row and stops at its ends, and on the second row goes between the battery and Turn off. On a level, it steps the brightness or the volume by 5 % |
| The pad's up or down | The ring walks the controls: down from Wi-Fi to the battery, from Settings to Turn off, from Bluetooth, the battery or Turn off to the brightness level, and from the brightness to the volume. Up is the same walk back. Nothing wraps |
| TOP, pressed and released | The control with the ring acts: Wi-Fi and Bluetooth switch the radio, Settings opens the Settings page, the battery opens the Battery page, Turn off asks "Turn off?". A level does nothing |
| TOP held, on Wi-Fi or Bluetooth | The page of the radio opens. Elsewhere, nothing |
| BOT, pressed and released | Back to the face. The screen goes up as a curtain |
| BOT held | Home: Apps' root, with no rescan |
| A tap on a circle | The ring moves there and the circle acts as TOP does |
| A finger held on the Wi-Fi or Bluetooth circle | The page of the radio opens. The lift does not switch the radio |
| A press or a drag on a level | The level follows the finger and is stored when the finger lifts |
| A swipe up, anywhere but on a level | Back to the face. The screen follows the finger up |
| A drag or a flick in any other direction | Nothing (by design) |
| No input for the auto-lock time | The auto-lock: the face comes down over Settings |
A page
Wi-Fi, Bluetooth, Settings, Screen, Battery, About, Kody, Self-test and the two lists of choices. gui_sheet_navigate() and gui_sheet_body() read them.
| Input | What happens |
|---|---|
| The pad's up or down | The ring moves one row, wrapping |
| The pad's left or right | The value of the row under the ring changes by one step. A row with no value does nothing |
| TOP, pressed and released | The row under the ring acts |
| TOP held | On the Wi-Fi page: forgets the network under the ring, when the board remembers it. Elsewhere nothing |
| BOT, pressed and released | The page goes back one level: a page to Settings, Settings, Wi-Fi, Bluetooth and the battery's Battery page to the control centre, a list of choices to its page |
| BOT held | Home. The settings screen goes back to Apps' root and the page goes with it. A busy sheet ignores the hold |
| A tap on a row | The ring moves to the row and the row acts |
| A press or a drag on a value row | The ring moves to the row and the level follows the finger |
| A swipe right on a page | The page goes back one level. A swipe that starts on the brightness bar is the bar's |
| A drag or a flick on the body | A page longer than the card scrolls and settles on a whole row. On Wi-Fi and Bluetooth the switch stays at the top |
| A touch on the blocker while the card slides | Nothing (by design) |
Rows with no value, where left and right do nothing: every row of the Wi-Fi, Bluetooth, Battery and About pages, the Auto-lock row, and every choice in a list. On the Settings list, Sleep after, Kody's scene and Vibration step through their values and wrap, and the switches While charging, Header 3.3 V and Header 5 V have no step. TOP or a tap changes any of them where it stands.
The keypad
The pad moves the focus over the controls of the card, and TOP presses the control with the focus. The ring marks the focus.
| Input | What happens |
|---|---|
| The pad's up or down | The focus moves one row: the yellow pill, the Show password row, and four rows of keys. It wraps |
| The pad's left or right | The focus moves one key along a row, wrapping. On the pill and the Show password row, nothing |
| TOP, pressed and released | The control with the focus is pressed |
| TOP held | Nothing (by design) |
| BOT, pressed and released | The keypad closes back to the Wi-Fi page. What was typed is wiped |
| BOT held | Home, as for any card. What was typed is wiped |
| A tap on a control | The focus moves to the control and the control is pressed |
| A drag down on the header, the top 92 rows of the card | The card follows the finger and closes. What was typed is wiped |
| A drag on the field or on the keys | Nothing (by design) |
Down from the pill or from the Show password row lands on the column where the focus last stood. The Show password row is skipped for a hidden network's name. While the join is out, the focus still moves, the pill is dimmed and no control acts.
A system card
The pad does nothing on a system card. One table serves them all, and the last column says where a card differs.
| Input | What happens |
|---|---|
| Any button or touch in the first 700 ms after the card began to rise | Nothing (by design). A press that began under the gate answers nothing even if its lift comes later |
| TOP released, on Didn't open or Stopped | The answer button acts: Try again or Run again |
| TOP released, or a tap, on Turn off | The red button "Turn off" confirms. On USB the button is grey and the board does not stop |
| BOT, pressed and released | The card closes. On Turn off it answers no. On the pairing code it refuses the passkey. On the write screen it does nothing |
| BOT held | Home, as for any card. Home closes a card that can be closed, over the face too, and the face stays the face. It is refused inside the gate, for a press that began before the card, and over the write screen |
| A tap on the button of Didn't open or Stopped | The answer button acts |
| A swipe down on Turn off | Answers no, at the lift |
| A drag, a swipe or a tap anywhere else | Nothing (by design). The recognizer has no zone on a system card |
A hold that reached its end acts once; the Ring stays full and takes no second hold until the card is closed.
The details
The details of an app open over Apps. gui_details_navigate() reads the buttons while they are up.
| Input | What happens |
|---|---|
| The pad | Nothing (by design) |
| TOP released | The Remove pill acts, for an installed app: it asks its question. On the question, the red Remove erases the app |
| TOP held | Nothing (by design). It opens no Settings |
| BOT released | The card closes. On the question of Remove it answers no and closes both cards |
| BOT held | Home: the card closes and Apps stays at its root |
| A tap on the pill | The pill acts, as TOP does |
| A drag down from anywhere on the card | The card follows the finger down and closes. It closes when the finger lifts past halfway, or faster than 800 px/s |
| Any touch on the Removing card | Nothing (by design). It is locked |
The hold that opened the details disarmed the press it grew out of, so its release is no answer. The question takes no answer for 400 ms after it is written.
The guided tests and the self-test
During a guided factory test the operator presses what a script asks for. Gui_btn_test and Gui_tp_test swallow BUTTON_EVT_SIG, BUTTON_HOLD_SIG, HOME_SIG and every door, because AO_Buttons, AO_Touch, AO_LedMgr and AO_Display score those very presses. Every input does nothing (by design).
While a run of the Self-test page lasts, AO_Gui is in Gui_selftest on the loading screen.
| Input | What happens |
|---|---|
| TOP at the hold | After "Now we need you", the run goes on to the rows that need hands |
| BOT before the hold or at it | The run is asked to stop after the check in flight and the line reads "Stopping after this check" |
| BOT after the hold | Nothing from AO_Gui. The row in flight reads the press as its answer |
| Any other press, a hold of either button, HOME, a door | Nothing (by design) |
Haptics
The interface buzzes at the moments that act, refuse and arm. The buzz is the LRA motor on the antenna board, which AO_Haptic drives. kgui_haptic_play(effect) in kgui_haptic.c is the one call every moment makes. It applies the level, keeps silent while a recording would pick the buzz up, spaces the step, and asks AO_Haptic by event with kode_haptic_play().
The setting haptic_ui sets how much of the interface buzzes: off, light or normal, default light. The Vibration row of the Settings list sets it too.
| Level | What plays |
|---|---|
off | Nothing |
light | click, thud, tick and mark |
normal | Light, and a step each time the focus moves, at most 14 a second (KGUI_HAPTIC_STEPS_PER_S) |
| Effect | Length | Strength | Means |
|---|---|---|---|
| click | 12 ms | 60 % | Something acted |
| thud | 25 ms | 80 % | A limit was reached, or an input was refused |
| tick | 6 ms | 40 % | A pull passed the point where letting go commits, or a hold began to fill its Ring |
| mark | 18 ms | 70 % | A hold reached its end and did something |
| step | 6 ms | 40 % | The focus moved one place (level normal only, at least 72 ms apart) |
The moments:
- click: a circle acts; Apps goes in on a row; the one answer of a card; a row of a page that acts; the Auto-lock row and each change of a Screen setting; a key of the keypad that typed, deleted or changed the mode; Kody leaves for the QR code and comes back.
- thud: the pad past the first or last row, and a finger that takes the list past an end, once for each pull; a key the full text refuses; BOT held where home is refused.
- tick: a settings pull passes the point where letting go commits; a card taken by its header passes the point where letting go closes it; a hold begins on a hold card, and the red arc starts to fill at 2 s.
- mark: BOT held for 0.6 s where home does something; a hold fills its Ring; the arc is full at 5 s; TOP held opens the details or the page of the Wi-Fi or Bluetooth circle.
- step: the pad moves the selection of Apps, the controls of Settings or the ring between the rows of a page; a tap on a row not yet selected; left or right steps the brightness bar.
A buzz that is not played is counted and said at debug level with the reason: haptic: <effect> held back (<reason>). The reasons are level (the level does not play this effect), recording (the microphone or the IMU records), pace (a step came less than 72 ms after the last) and AO_Haptic is busy, no event in the pool or AO_Haptic refused it. The silence for a recording follows the rows of AO_Autotest alone: AUTOTEST_ROW_SIG raises it at the start of the audio, IMU or haptic row and the verdict lowers it. A recording nobody started through a row is not seen.
config key="haptic" is a percentage AO_Haptic applies to every gain it is asked for, the interface's included, so haptic=0 silences the interface as well. haptic_ui=off is the setting meant for the interface alone.
gui_info ends its interface line with ui: haptics <level> (silenced by <nothing|the microphone|the IMU|both>), motion <word>, awake on USB <on|off>; <n> buzzes asked, <m> held back. The bench board 6c:0a has no AW86233, so its evidence is the count. While a secret is typed, kgui writes no haptic: line for a buzz, but the counts still count.
The four rules
The rules behind every table above.
| Rule | Meaning |
|---|---|
| 1. The pad acts at the press, the side buttons at the release | Seeing a side button's press arms it. A hold disarms the press it grew from. A side button acts only when it has seen both edges, so a stray edge, such as electrical noise on GPIO35 that the 30 ms debounce confirmed, never acts alone. AO_Gui times no press; AO_Buttons declares the hold |
| 2. A swallowed input disarms the press behind it | Pressing the other button, a finger landing, a hold, a screen leaving the glass, a curtain starting, a sheet opening, a rescan and a busy sheet's refusal all call gui_press_disarm(). On the face and Apps a pad button and the other side button disarm; on Settings only the other side button; on a sheet no button |
| 3. A cursor that cannot move changes nothing | Apps refuses at its ends and stops the repeat. A ring that would wrap onto itself stays |
| 4. Only a tap acts, and a press that travels is not a tap | Every click handler that acts asks gui_click_is_tap(): an Apps row, a settings circle, a page row, a keypad control, the answer button of a card, and Kody on the empty launcher. A press is a tap when the finger never went farther than 16 px from where it landed and the recognizer did not claim it. LVGL's own distance plays no part |
A press that is not a tap acts on nothing. It logs not a tap: the press on <what> was <outcome>; nothing acts at debug level, with the recognizer's word: claim, diagonal, axis, zone or blocked.
A held button counts as input for as long as it is down. Nothing publishes while a hand rests on one, so the face, Apps, Settings and any card over them track which buttons are down (btn_held, gui_btn_track()), and the auto-lock and the rest hold their counts meanwhile.
The recognizer
Every touch sample goes through the recognizer of the kit (kui_gesture, sdk/kui) before LVGL reads it. The recognizer decides what a press is, from where the finger has gone, and it owns every door and every tap: the curtains a finger pulls or catches, the card a finger takes down by its header, the way back a swipe right makes, the Settings pull over Apps, and the tap on the face and on every row, circle and button. LVGL keeps one thing: the scroll of the list page of a card. The rail of Apps is the interface's own, so no LVGL scroll is involved.
Each sample is measured from the point where the finger landed, never from the sample before, so the answer does not depend on how often the touch sensor reports.
| Outcome | Rule |
|---|---|
| Tap | The finger never went farther than 16 px (KUI_GESTURE_TAP_PX) from the landing, on either axis, and then lifted |
| Claim | The first sample 12 px or more from the landing (KUI_GESTURE_CLAIM_PX) decides the press. The farther axis must lead the nearer one by at least 1.5:1, and the zone must take that direction. The claim keeps its axis |
diagonal | The axes are under 1.5:1 |
axis | The zone takes other directions only |
zone | The press landed where no drag is born |
blocked | Input was shut when the finger landed. A touch at Rest on the battery counts here too |
A rejection is noted at first and becomes the state only when the finger goes farther than 16 px, so a sloppy tap still works where nothing drags. The input device of AO_Gui starts a scroll one pixel after the claim distance, at 13 px, so the recognizer has decided before a scroll can begin.
gui_gesture_zone() says what a press may become. It reads the screen, the card over it and the landing point on the press edge, once.
| Where the press lands | Directions it takes | What a claim is for |
|---|---|---|
| A guided test, or the locked card of a running write | None: blocked | - |
| A curtain, while its spring settles | Up and down | A catch: close, settings or unlock |
| A curtain a finger holds, or one that has landed and waits | None: blocked | - |
| The Turn off card | Down | dismiss: a swipe down answers no, at the lift |
| Any other system card | None | - |
| A card that a finger or a spring has, or that slides | None: blocked | - |
| A settings page | Right | back: one level. A press on the brightness bar takes no drag |
| A card's header (top of the card to the foot of the title disc; the keypad's 92 rows; all of the details) | Down | sheet: the card follows the finger |
| A card, anywhere else | None | A list page scrolls under LVGL |
| The face | Down | settings: the curtain. A swipe up or sideways is zone |
| The face at Rest | None: Gui_rest swallows the touch first | The press counts as blocked |
| Apps, with rows | Up and down; right, and left at the root | scroll, the Settings pull for down from the top 44 rows or when its conditions hold, and level sideways: back |
| Apps, while the rows slide, or with no rows | None; right only | Back to the face |
| The settings screen | Up | close |
The top band. The first 60 rows of the glass are the top band. The corners are cut away, so at each row the band starts where the glass does. The band draws nothing of its own and takes no press: the content may stand in it, and a tap there is the content's tap. The hint stands in it. The face's drag down starts anywhere on it, and over a card a drag down from the band is the card's.
At the end of every press AO_Gui logs one line at debug level (gui_gesture_end()), such as gesture: claim up scroll at 205,400, 0,-180 px or gesture: tap at 205,300, 0,0 px. While a secret is typed, every gesture line is written without coordinates. The list logs its own lines at INFO level: gesture: caught the list at <p>, gesture: list released at <v> px/s -> row <r>. A curtain logs gesture: caught the curtain at <p>% and gesture: release at <p>% <v> px/s -> open|back, and a card logs gesture: card release at <p>% <v> px/s -> stay|close.
The counters. gui_info reports >|gesture. The counters count presses since boot and stop at 65535:
taps,claimsand the four rejectionsdiagonal,axis,zoneandblocked.commits: curtains and cards a finger followed that landed on the far side.shortscounts the ones that went back to where they started.commits+shortsnever exceedsclaims.
Gotchas
Example code
Walk the screens with the buttons:
- Boot the Kode Dot. The face shows. Press TOP and let go. Apps slides in from the right.
- Press BOT and let go. The face slides back in from the left. Press the pad's up button. Settings comes down over it. Press BOT to close it.
- Press the pad's right button. Apps opens. Move the pad's down button until an app is in focus, then hold TOP for a moment. The details of the app open. Press BOT to close them.
- On the face, press BOT and let go. The face dims. Press BOT: "Press ● twice" shows. Press TOP, then TOP again, to wake the face.
- On the face, press and hold BOT. From 2 s a red arc fills along the top, and at 5 s "Turn off?" rises. Press BOT to answer no.
In the SDK
kgui_ao.c: the button maps,gui_gesture_zone(),gui_off_arc_tick()andgui_click_is_tap().kgui_haptic.c: the level, the silences and the pace of the buzzes.kui_gesture.h: the recognizer.kqp_config.h:KODE_BUTTON_HOLD_MSand the other timing constants.components/kaos/buttons: AO_Buttons, the owner of the six buttons.