Skip to content

Source: InputMapping.h

InputMapping

The binding table behind every input service: a row says what one input does to one control.

Shared by button, infrared and every later transport, because the half that differs between them is only how the event is detected. What happens next is identical, so it lives here once rather than in each module.

Functions

Return Name Description
bool firePadRow inline Fire the pad in a grid position, false when there is nothing there: why through the list.
bool runInputAction inline Apply an action to its target through the one primitive every transport uses, false when the row is unassigned or names something absent.
bool runInputLevel inline Drive a target with a continuous value, rescaled into the control's own range: why this is separate from the event path.
void writeInputActionFields inline The action half of a row as a document, emitted by every service so one row shape renders wherever it came from.
bool targetTypeIsNumbered inline Whether a target type is numbered; a named test, so a future unnumbered type reads clearly.
void composeTarget inline Build the stored target string from a type and a number, the two being how a user edits it rather than how it is kept.
void decomposeTarget inline Read a stored target back into a type and a number for the editor: what an unrepresentable one reads as.
void writeInputTargetOptions inline The target-type options as a shared set, emitted once per list rather than per row.
void writeInputTargetDetailField inline The target half alone, for an input whose value is the reading: a kind and a value would be stored, shown and ignored.
void writeInputActionDetailFields inline The action half as editable fields, so a user retargets an input without the interface: why a Set is not always offered.
bool setInputActionField inline Set one action field from a row edit; false for a field this does not own, so a module can try its own afterwards.

firePadRow

inline

inline bool firePadRow(MoonModule & mod, uint8_t slot, const char * label, char * outStatus, size_t statusLen)

Fire the pad in a grid position, false when there is nothing there: why through the list.


runInputAction

inline

inline bool runInputAction(const InputAction & a, bool pressed, char * outStatus, size_t statusLen)

Apply an action to its target through the one primitive every transport uses, false when the row is unassigned or names something absent.


runInputLevel

inline

inline bool runInputLevel(const InputAction & a, uint8_t level, char * outStatus, size_t statusLen)

Drive a target with a continuous value, rescaled into the control's own range: why this is separate from the event path.


writeInputActionFields

inline

inline void writeInputActionFields(JsonSink & sink, const InputAction & a)

The action half of a row as a document, emitted by every service so one row shape renders wherever it came from.


targetTypeIsNumbered

inline

inline bool targetTypeIsNumbered(uint8_t type)

Whether a target type is numbered; a named test, so a future unnumbered type reads clearly.


composeTarget

inline

inline void composeTarget(char * out, size_t outLen, uint8_t type, uint8_t number)

Build the stored target string from a type and a number, the two being how a user edits it rather than how it is kept.


decomposeTarget

inline

inline void decomposeTarget(const char * target, uint8_t & type, uint8_t & number)

Read a stored target back into a type and a number for the editor: what an unrepresentable one reads as.


writeInputTargetOptions

inline

inline void writeInputTargetOptions(JsonSink & sink)

The target-type options as a shared set, emitted once per list rather than per row.


writeInputTargetDetailField

inline

inline void writeInputTargetDetailField(JsonSink & sink, const InputAction & a)

The target half alone, for an input whose value is the reading: a kind and a value would be stored, shown and ignored.


writeInputActionDetailFields

inline

inline void writeInputActionDetailFields(JsonSink & sink, const InputAction & a, bool hasRelease = true)

The action half as editable fields, so a user retargets an input without the interface: why a Set is not always offered.


setInputActionField

inline

inline bool setInputActionField(InputAction & a, const char * field, const char * valueJson)

Set one action field from a row edit; false for a field this does not own, so a module can try its own afterwards.

Variables

Return Name Description
constexpr int kMinActionValue constexpr What a row's value may hold, stated once and used by both the editor's bounds and the field that parses an edit.
constexpr int kMaxActionValue constexpr
constexpr unsigned long kMaxPadNumber constexpr The largest pad a target may name; a number past it addresses nothing, so it is refused rather than wrapped.
constexpr const char * kTargetTypes constexpr The target types an input can point at, a type plus a number: why only the surface.
constexpr uint8_t kTargetTypeCount constexpr
constexpr unsigned long kTargetTypeMaxNumber constexpr The highest number each target type has, indexed by type, so a parse cannot accept a control that does not exist.

kMinActionValue

constexpr

constexpr int kMinActionValue = -32768

What a row's value may hold, stated once and used by both the editor's bounds and the field that parses an edit.


kMaxActionValue

constexpr

constexpr int kMaxActionValue = 32767

kMaxPadNumber

constexpr

constexpr unsigned long kMaxPadNumber = 64

The largest pad a target may name; a number past it addresses nothing, so it is refused rather than wrapped.


kTargetTypes

constexpr

constexpr const char * kTargetTypes = {
    "",           
    "switch",     
    "encoder",    
    "fader",      
    "pad",        
}

The target types an input can point at, a type plus a number: why only the surface.


kTargetTypeCount

constexpr

constexpr uint8_t kTargetTypeCount = sizeof(kTargetTypes) / sizeof(kTargetTypes[0])

kTargetTypeMaxNumber

constexpr

constexpr unsigned long kTargetTypeMaxNumber = {
    0,               
    8,               
    8,               
    8,               
    kMaxPadNumber,   
}

The highest number each target type has, indexed by type, so a parse cannot accept a control that does not exist.

InputAction

struct InputAction
src/core/util/InputMapping.h:86

What one physical input does: a target control, and how the input changes it.

what a target may name.

Public Attributes

char target = "" : "Module.control", empty for an unassigned row

Kind kind = : which of the three this row does

int16_t value = 0 : Set: what to write. Delta: the signed nudge. Toggle: unused.

Public Methods

inline bool assigned() const : Whether this row names a target at all.

Public Types

enum Kind : How the input changes its target; a toggle is its own kind because a delta cannot express it.

Value Description
Toggle read the current value, write its inverse. A light switch.
Set write value. A momentary hold writes 1 then 0; a pad writes a slot.
Delta add value to the target, clamped to its declared bounds. A nudge.

More info

A row names Module.control, so an input can drive the control surface or a module control directly. Both are the same mechanism with no special case: the surface is a recommendation rather than a rule. Driving the surface is the better path, since one place then shows what the device's controls do and every transport reaches the same switch.

The editor offers only the surface. An earlier draft also offered a few module controls directly, which was a second path to the same place. Two ways to say one thing is the split brain the two-step model exists to avoid, and it left a user wondering which a given row used. A row can still name any control through the interface, which is the escape hatch for anything the surface does not carry yet.

A pad is a row, not a control

A pad grid renders from a list, so a pad is reached through that list rather than by control name. Every such list already publishes each row's slot and accepts an activate field, so this needs to know nothing about presets. Any module that grows a pad grid therefore becomes targetable by every input at once. Resolving it here rather than in each service is what makes a button, a remote and every later transport reach a pad the same way.

A pad fires on the press alone. A release firing it again would re-apply the same preset for no reason, and would make a momentary row unusable.

An event and a reading are different shapes

A press carries no number, so a Set row writes the fixed value it was given and zero on release. An analog input's value IS the reading, so a row's stored value has nothing to say, and the two paths stay separate rather than sharing a parameter. They differ in what they do with every kind: a toggle or a delta driven fifty times a second is not something a user can mean.

A reading arrives in the range every surface control uses and is rescaled to whatever the target holds. So a pedal is configured once and works on any target, rather than needing a range per target.

Reading and writing in the control's own units

A value is read through the scheduler, which answers at the control's declared width. Reading the raw pointer as a byte was wrong, a wider control holding a large value reading back truncated. The surface's own byte reader clamps too, so a positive delta wrote the wrong number and a negative one could never move a control down. A mapping nudges the CONTROL rather than the surface, so it reads in the control's units and clamps to the control's own bounds.

A select and a palette store their option COUNT, so the last valid index is one below it. Clamping to the count produced a value the writer clamped again, and a delta that overshot stopped short of the end instead of landing on it.

A Set needs a release to clear it

A button reports letting go; a remote does not, since a code arrives as a single event with no matching release. So a Set is offered only where a release exists to clear it, or it would write its value and latch forever. An input without one offers toggle and delta, which are both complete in one event.

A target the vocabulary cannot express does nothing

The whole suffix of a name must parse, and the number must be in that type's own range. A trailing character would otherwise fire the wrong pad, and a number past the range would wrap through the cast and fire another one entirely. One shared bound would let a number past the surface's count through: it parses, it is stored, and it dispatches to nothing.

A target set through the interface to something the editor cannot represent reads back as unassigned, so the dropdown shows nothing while the row keeps working. That is the honest reading, and silently rewriting it would be worse.