SOFTrobots OS · App-API

Stand 10. November 2026 · App-API 1 · Entwurf bis Release 1.0.0 – Deklarationen können sich noch ändern; Programme werden dann neu gebaut, nicht kompatibel gehalten · 1946 öffentliche Deklarationen (vollständige Liste seit 10.11.) in docs/api/app-api-1.txt

Wie ein Programm Rechte bekommt

Nur über sein signiertes Manifest: capabilities = … nennt die Rechte, der Kern prüft jeden Aufruf dagegen. Zeilen wie connects =, listens =, opens =, realtime = grenzen weiter ein. Was die Person im Datei-Dialog wählt, wird eine Freigabe für genau diese Datei – „Wählen ist Erlauben“.

Wie ein Programm Bibliotheken bekommt

Über Profil-Dateien im Programmordner (uses-ui, uses-heap, uses-image, uses-tls …). Gebaut wird mit dem SDK (srapp), die Laufzeit heißt SOFTrobotsKit. Ohne uses-heap gilt das Echtzeit-Profil ohne Heap.

So liest man diese Seite

Je Thema: wofür, welches Recht, dann die Aufrufe mit Signatur, einer Zeile Erklärung und dem Systemaufruf dahinter (Modul.aufruf, Kennung, Budget). neu = seit heute (Datei-Dialog, Dateimenü, Fortschritt, dock = no). Unter wichtigen Aufrufen steht ein zugeklapptes Beispiel, meist aus echten Programmen (apps/…), sonst als „Muster“ markiert; je Thema folgen längere Beispiele.

Wie lange ein Aufruf dauert

Das ≤ 2 ms-Feld ist das Zeitbudget aus shapes/*.shape: die längste Laufzeit, die der Kern für diesen Shape-Aufruf zusagt (gemessen wird dagegen bei jedem Aufruf). Reine Bibliotheksaufrufe ohne Systemaufruf haben kein Budget.

19 Themen · 507 Einträge, davon 422 Swift-Aufrufe/Typen · 138 Shape-Aufrufe in 24 Modulen · 242 Beispiele

Rechte & Manifest

Ein Programm bekommt Rechte nur über sein Manifest (Datei manifest im Programmordner, signiert beim Bauen). Die Zeile capabilities = … nennt die Rechte; der Kern prüft jeden Aufruf dagegen. Ein unbekanntes Recht macht das Manifest ungültig – ein Programm bekommt nie ein Recht, das der Kern nicht versteht. Ein Aufruf ohne Rechtevermerk in dieser Übersicht ist jedem Programm offen.

Dazu kommen die Profil-Dateien uses-… im Programmordner: Sie entscheiden, welche Bibliotheken mitgebaut werden (Heap, Oberfläche, Bilder, TLS …). Ohne uses-heap ist ein Programm im Echtzeit-Profil (-no-allocations).

Manifest-Zeilen

EintragBedeutungShape-Aufruf · Zeitbudget
Identität
name = helloProgrammname (Pflicht).
id = de.softrobots.helloIdentität als umgekehrte Domain (Pflicht seit 08.11.); Datenordner ist /data/<id>.
version = 1.0.3Version (Pflicht); ersetzt wird ein Programm nur durch eine höhere.
api = 1App-API-Version, für die gebaut wurde; ein System, das weniger bietet, startet es nicht.
Rechte & Dokumente
capabilities = system.log, ui.surfaceDie Rechte des Programms (Tabelle unten).
opens = txt, mdDokumenttypen, die das Programm öffnet (bis 16); Voraussetzung für Doppelklick in Files und File.openDocument.
Start & Anzeige
dock = no neuNie im Dock zeigen, auch nicht während es läuft (der Datei-Dialog chooser).
start = demand | updateNicht beim Booten starten, nur auf Anfrage (bzw. beim ersten Start einer neuen Systemkopie).
helper = videoDient nur einem anderen Programm; wird in Listen nicht angeboten.
title / category / iconAnzeigename, Gruppe (system, productivity, media, robot, tools) und SVG-Symbol 24 × 24.
Speicher
memory = 512Heap-Grenze in MiB (16 … 2048, Standard 256); mit der Zeile auch eine Garantie.
stack = 4096Stapelgröße in KiB (64 … 8192).
Echtzeit & Daten
realtime = 1000 200 stopEchtzeit-Vertrag: Periode und Budget in µs, Verhalten bei Fristverletzung (log, stop, restart, stop-actuators).
record = samples 64 5 3600 [flight]Eine Messreihe ohne Systemaufrufe (Recorder); flight = Flugschreiber.
publish = detectionsEine Veröffentlichung, die andere ohne Systemaufrufe lesen (Erkennungs- oder Arm-Ring).
subscribe = video.detectionsBis zu zwei Veröffentlichungen anderer Programme, beim Start nur lesbar eingeblendet.
Netz
connects = api.anthropic.comDie einzigen Hosts, die das Programm erreichen darf.
listens = 443, 80TCP-Ports zum Lauschen (mit network.incoming, bis 8).

Rechte (capabilities)

EintragBedeutungShape-Aufruf · Zeitbudget
System
system.logZeilen ins Systemprotokoll schreiben.≤ 5 µssystem.log0x0002_0003
system.log.readDas Systemprotokoll lesen (Konsole, Activity).≤ 20 µssystem.readLog0x0002_0005
system.statusSystemzustand lesen: Belegung, Prozessoren, Verbrauch aller Programme.≤ 100 µsprograms.usage0x0006_000E
system.timeDie Systemuhr stellen (Zeitdienst nach NTS).≤ 5 µssystem.adjustClock0x0002_000A
system.powerAusschalten und Neustarten.ohne Budgetsystem.power0x0002_0004
system.protectionGeräteschutz: Passwort, Entsperren, Bildschirmsperre, MIC-Anzeige (nur Display-Dienst).≤ 5 µsprotection.status0x0007_0001
system.updateSignierte Systemupdates in die ruhende Systemkopie installieren.≤ 60 supdate.install0x0008_0002
system.privacyGeheimnisse wie API-Schlüssel für Programme setzen (Settings).≤ 5 msprograms.setSecret0x0006_0004
Bildschirm & Eingabe
ui.surfaceFenster zeigen (bis zu drei).≤ 5 msdisplay.createSurface0x0011_0004
display.ownDen Bildschirm besitzen (nur der Display-Dienst).≤ 5 msdisplay.open0x0011_0001
input.provide / input.receiveEingabe-Ereignisse liefern (Treiber) bzw. empfangen (Display-Dienst).≤ 5 µsdisplay.submitInput0x0011_0006
Dateien & Datenbank
files.ownDateien im eigenen Ordner /data/<id>; sonst existiert nichts.≤ 2 msfiles.open0x0003_0001
files.systemDas ganze DATA-Volume (/apps nur lesend), Snapshots.≤ 50 µsfiles.snapshot0x0003_000C
files.macZusätzlich den Mac-Ordner mac/… (nur SOFTrobotsVM).
files.dialog neuDen Öffnen-/Sichern-Dialog des Systems zeigen und die Wahl melden (nur Programm chooser).≤ 5 msdialog.answer0x0013_0004
database.editJede Datenbank auf DATA schreiben, nicht nur die eigenen (Programm Database).≤ 1 msdb.write0x0004_0002
Programme & Kanäle
apps.launchProgramme starten, stoppen, auflisten, Dokumente übergeben, Abstürze lesen.≤ 50 msprograms.launch0x0006_0007
ipcKanäle zwischen Programmen öffnen.≤ 1 mschannels.open0x000A_0001
develop.buildProgramme aus Skripten mit dem Schlüssel dieses Roboters bauen (Develop).≤ 3 sprograms.build0x0006_0010
Netz
network.outgoingAusgehende TCP-Verbindungen und UDP; mit connects = eingegrenzt.≤ 5 snet.connect0x0005_0004
network.incomingVerbindungen auf den Ports der listens =-Zeile annehmen (Programm Server).≤ 50 µsnet.listen0x0005_000C
Ton & Mikrofon
sound.playTon abspielen: ein Ring, den der Mischer des Kerns spielt.≤ 1 mssound.open0x0030_0006
sound.volumeDie Gesamtlautstärke setzen.≤ 50 µssound.setVolume0x0030_0009
device.microphoneMikrofon hören; die rote MIC-Anzeige nennt jedes Programm, das hört.≤ 1 msmicrophone.open0x0031_0002
microphone.provideEin Mikrofon anbieten (Test-Mikrofon).≤ 1 msmicrophone.provide0x0031_0006
Kamera
device.cameraKameras auflisten und ihre Bildringe lesend einblenden.≤ 1 mscamera.ring0x0020_0006
camera.provideEine Kamera anbieten und Bilder hineinschieben (Test-Kamera).≤ 1 mscamera.provide0x0020_0007
Roboter & Treiber
safety.stopDen Not-Halt auslösen und lösen.≤ 20 µssafety.stop0x0100_0002
safety.actuatorAktoren treiben: bei Not-Halt sofort geweckt, muss sie sicher machen (Aktor-Dienst).
device.usb / device.canTreiber: Register eines USB- bzw. CAN-FD-Controllers einblenden, DMA-Speicher.≤ 1 msdevices.open0x0009_0001

Profile (Dateien uses-…)

EintragBedeutungShape-Aufruf · Zeitbudget
Profile (Dateien uses-…)
(keine Datei)System, Files, Channel, Net, Apps, Settings, TextLine, WallClock, FileType, Maths – Echtzeit-tauglich, ohne Heap.
uses-heapArrays, Strings, Klassen; dazu die Ton-Bibliothek (WAV, FLAC, Pegel, Stimmerkennung).
uses-uiSurface, Canvas, Views, Keyboard, InputField, UITheme, Chart, ScrollBar.
uses-imagePicture, PNG, JPEG, GIF, WebP, BMP, TIFF, PDF schreiben/lesen, Matroska (braucht uses-heap).
uses-archiveZIP lesen/schreiben mit WinZip AES-256, Deflate (braucht uses-heap).
uses-tlsTLS 1.3 (SecureConnection), DNS über HTTPS, Krypto, Vertrauensanker.
uses-aiClaude (Messages API) – braucht uses-tls und uses-heap.
uses-video / uses-opusVP9-Decoder / Opus-Decoder (braucht uses-heap und uses-image).
uses-svg / uses-web / uses-sqlSVG-Renderer; Browser-Code; SQL-Übersetzer für Datenbanken (nur lesend).
uses-vision / uses-inferAprilTag, Farbflecken, Bewegung / int8-Inferenz (Detektoren).
uses-qr / uses-httpserverQR-Code-Encoder / HTTP/1.1-Server-Bausteine.
uses-reflex / uses-reflexrtReflex-Compiler und -Maschine (Develop) / Echtzeit-Laufzeit.
Beispiel: Manifest eines Programms mit Fenster, eigenen Dateien und Datei-Dialog (apps/reader, apps/dialogcheck)
# Reader: shows PDF documents.
name = reader
id = de.softrobots.reader
title = Reader
category = productivity
icon = icon.svg
capabilities = system.log, ui.surface
kind = system
opens = pdf
start = demand
memory = 768
# Programmordner daneben: uses-heap, uses-image, uses-svg, uses-ui
Beispiel: Der Datei-Dialog selbst: nie im Dock (apps/chooser/manifest)
name = chooser
id = de.softrobots.chooser
title = File Dialog
category = system
capabilities = system.log, ui.surface, files.system, files.dialog
kind = system
start = demand
helper = display
# Never in the dock, not even while a dialog is open (2026-11-10).
dock = no

Fenster & Oberfläche

Ein Fenster ist eine Surface: Pixelspeicher und ein Nachrichtenring, die der Kern ins Programm und in den Display-Dienst einblendet. Das System zeichnet Rahmen, Titel und Menüleiste; das Programm zeichnet seinen Inhalt mit Canvas oder beschreibt ihn mit Views (VStack, Text, Button …), Farben kommen aus UITheme. Die Ereignisschleife: nextEvent() bis nil, zeichnen, present, wait. Jedes switch über SurfaceEvent braucht default: (neue Fälle kommen dazu).

SurfaceFenster des Programms

AufrufWas er tutShape-Aufruf · Zeitbudget
Fenster anlegen
static create(timeout: UInt64 = 10_000_000_000) -> Surface?Fordert ein Fenster an (bis zu drei pro Programm), wartet notfalls auf den Display-Dienst.≤ 5 msdisplay.createSurface0x0011_0004
Beispiel
guard var surface = Surface.create() else { System.log("text: no window"); return }
aus apps/text (gekürzt)
requestSize(width: Int, height: Int)Gewünschte Inhaltsgröße (der Nutzer kann sie ändern).
Beispiel
surface.requestSize(width: 1120, height: 720)
aus apps/sound (gekürzt)
setTitle(_ text: TextLine)Name über dem Fenster.
Beispiel
window.setTitle(TextLine("Web"))
aus apps/web (gekürzt)
destroy()Schließt eines von mehreren Fenstern (bei nur einem: einfach beenden).
Beispiel
guard var second = Surface.create() else { return }
// ... später, bei .close dieses Fensters:
second.destroy()
Muster (kurz, nicht aus einem Programm kopiert)
Ereignisse holen
mutating nextEvent() -> SurfaceEvent?Nächste Nachricht des Dienstes, nil wenn keine wartet; .configure passt canvas an.
Beispiel
while let event = surface.nextEvent() {
    switch event {
    case .configure: dirty = true
    case .close: System.exit(0)
    default: break
    }
}
aus sdk-apps/greeter (gekürzt)
wait(timeout: UInt64)Schläft, bis der Dienst etwas schickt oder timeout ns vergehen.wait (Systemaufruf 3)
Beispiel
surface.wait(timeout: 500_000_000)   // 0,5 s oder bis ein Ereignis kommt
aus apps/reader (gekürzt)
Zeichnen & zeigen
private(set) var canvas: CanvasDie Zeichenfläche, die der Dienst gegeben hat (leer bis zum ersten configure).
beginDrawing()Vor einem Bild aufrufen: der Dienst kopiert das Fenster erst nach present (kein Reißen).
Beispiel
surface.beginDrawing()
draw(surface.canvas)
surface.presentChanges()
aus apps/reader (gekürzt)
present(_ rect: Rect) -> BoolZeigt das geänderte Rechteck; false wenn der Ring voll blieb (später erneut zeigen).
Beispiel
surface.present(Rect(x: 0, y: 0, width: surface.canvas.width, height: surface.canvas.height))
aus sdk-apps/greeter (gekürzt)
presentChanges()Zeigt alle seit beginDrawing geänderten Kacheln (DamageTracker).
Zeiger, Vordergrund, Ziehen
mutating setPointer(_ shape: PointerShape)Zeigerform über dem Inhalt (.hand, .text, .arrow, .resize…).
Beispiel
surface.setPointer(ui.button(at: x, y) != 0 ? .hand : .arrow)
aus sdk-apps/greeter (gekürzt)
raise()Holt das Fenster nach vorn (z. B. nach einem übergebenen Dokument).
Beispiel
if openHandedOver() { surface.raise(); dirty = true }   // Doppelklick in Files: nach vorn
aus apps/database (gekürzt)
carry()Lässt den Nutzer ein Dokument ziehen; Antwort kommt als .dropped.
Beispiel
case .pointer(let x, let y, let buttons):
    if buttons & 2 != 0 && overDocument(x, y) { surface.carry() }
case .dropped(let program, let length, _, _):
    if length > 0 { /* über program[0..<length] losgelassen */ }
Muster (kurz, nicht aus einem Programm kopiert)
Interna
let handle: UInt64Nummer des Fensters (für Systemaufruf surfaceNotify).
static let frameCounter / frameShownByte-Offsets im Ringblatt: Bildzähler (ungerade = wird gezeichnet) und zuletzt gezeigtes Bild.

SurfaceEventWas der Dienst dem Programm meldet

AufrufWas er tutShape-Aufruf · Zeitbudget
Fenster
case configure(width: Int, height: Int)Größe geändert / Fenster bereit: neu zeichnen.
case focus(Bool)Fenster bekommt/verliert den Fokus.
case closeNutzer hat das Fenster geschlossen (⌘W): Programm beenden.
Eingabe
case key(code: UInt8, down: Bool)Taste (HID-Code); durch Keyboard.handle schicken.
Beispiel
case .key(let code, let down):
    let press = keyboard.handle(code: code, down: down)
aus apps/reader (gekürzt)
case pointer(x: Int, y: Int, buttons: UInt32)Zeiger bewegt / Taste; Bit 1 (Wert 2) = Primärtaste gedrückt.
case scroll(x: Int, y: Int, tenths: Int)Mausrad in Zehntel-Rasten, positiv vom Nutzer weg.
Beispiel
case .scroll(let x, let y, let tenths):
    scrollY = max(0, scrollY - tenths * 6)
aus apps/reader (gekürzt)
Menü & Ziehen
case menu(id: Int)Ein Menüpunkt (Menüleiste, Kontextmenü oder Kürzel) wurde gewählt.
case dropped(program: InlineArray<32, UInt8>, length: Int, x: Int, y: Int)Getragenes Dokument wurde über einem Programm losgelassen.

CanvasZeichnen in Pixeln (logische Punkte × scale)

AufrufWas er tutShape-Aufruf · Zeitbudget
Flächen
fill(_ rect: Rect, _ color: RGB)Rechteck füllen.
Beispiel
canvas.fill(Rect(x: 0, y: 0, width: canvas.width, height: 40), UITheme.raised)
Muster (kurz, nicht aus einem Programm kopiert)
gradient(_ rect: Rect, from top: RGB, to bottom: RGB)Senkrechter Verlauf.
blend(_ rect: Rect, _ color: RGB, alpha: UInt32)Halbtransparent überlagern.
Beispiel
canvas.blend(Rect(x: x - thick / 2, y: y - thick / 2, width: thick, height: thick), colour, alpha: alpha)
aus apps/develop (gekürzt)
smoothRect(_ rect: Rect, radius: Int, top: RGB, bottom: RGB)Abgerundetes Rechteck mit Verlauf, kantengeglättet.
Text
text(_ line: TextLine, x: Int, y: Int, color: RGB, scale requested: Int = 1) -> IntText in der Bitmap-Schrift; gibt die Breite zurück.
text(_ text: TextLine, x: Int, baseline: Int, face: FontFace, color: RGB) -> IntText in einer Systemschrift (.body, .heading …) auf eine Grundlinie.
Beispiel
canvas.text(TextLine("RECORDINGS"), x: area.x + 16, baseline: area.y + 26, face: .caption, color: UITheme.muted)
aus apps/sound (gekürzt)
static width(_ text: TextLine, face: FontFace) -> IntBreite eines Texts in einer Schrift.
Beispiel
let plus = Rect(x: minus.right + 24 + Canvas.width(TextLine("100 %"), face: .body), y: soundY - 21, width: 32, height: 32)
aus apps/sound (gekürzt)
static textWidth(_ line: TextLine, scale: Int = 1) -> IntBreite in der Bitmap-Schrift.
Pixel & Farben
pixel(_ x: Int, _ y: Int) -> UInt32 / setPixel(_:_:_:)Einzelnen Punkt lesen/setzen (im Bildschirmformat).
encode(_ color: RGB) -> UInt32 / decode(_ value: UInt32) -> RGBFarbe ↔ Pixelwert.
copy(from base: UInt64, stride sourceStride: Int, rect: Rect, to x: Int, _ y: Int, limit: Rect)Pixel aus einem anderen Speicher kopieren (Bilder, Video).
Teilflächen
region(_ rect: Rect) -> CanvasTeilfläche als eigene Canvas (Ursprung verschoben, beschnitten).
Beispiel
let sidebar = canvas.region(Rect(x: 0, y: 0, width: 210, height: canvas.height))
sidebar.fill(Rect(x: 0, y: 0, width: 210, height: canvas.height), UITheme.ground)
Muster (kurz, nicht aus einem Programm kopiert)
clip(_ rect: Rect) -> RectRechteck auf die Fläche beschneiden.
scaled(_ scale: Int) -> CanvasDieselbe Fläche mit anderem Maßstab.
Views zeichnen
render<V: View>(_ view: V, in rect: Rect, _ ui: inout UI, background: RGB = UITheme.background)Views messen und zeichnen; Buttons werden in ui eingetragen.
Beispiel
canvas.render(view, in: Rect(x: 0, y: 0, width: canvas.width, height: canvas.height), &ui)
aus sdk-apps/greeter (gekürzt)
Maße
let width, height, scale, stride, pixelWidth, pixelHeight, baseMaße (logisch und physisch) und Speicheradresse.

ViewsInhalt beschreiben statt zeichnen

AufrufWas er tutShape-Aufruf · Zeitbudget
Anordnen
VStack(spacing: Int = 8, @ViewBuilder _ content: () -> Content)Untereinander anordnen.
Beispiel
let view = Padded(20) {
    VStack(spacing: 14) {
        Text("greeter", face: .heading)
        Button("Count", id: Buttons.count)
        Spacer()
    }
}
aus sdk-apps/greeter (gekürzt)
HStack(spacing: Int = 8, @ViewBuilder _ content: () -> Content)Nebeneinander anordnen.
Padded(_ amount: Int, @ViewBuilder _ content: () -> Content)Innenabstand um einen Stapel.
Spacer() / Divider()Dehnbarer Leerraum / Trennlinie.
Inhalte
Text(_ text: StaticString | TextLine, face: FontFace = .body, color: RGB = UITheme.text)Ein Text in Systemschrift.
Button(_ title: StaticString | TextLine, id: Int, prominent: Bool = true, tint: RGB = UITheme.accent)Knopf; seine id liefert ui.button(at:).
ProgressBar (value, maximum, color)Fortschrittsbalken im Inhalt (für Aufgaben im Titel: Fortschritt).
Eigene Views
protocol View: measure(_:) / draw(in:on:_:) / flexible / compressibleEigene Views: messen und zeichnen.

UIKnöpfe, Fokus, Tastatur-Navigation

AufrufWas er tutShape-Aufruf · Zeitbudget
Knöpfe
init()Leerer Zustand (global halten).
button(at x: Int, _ y: Int) -> Intid des Knopfs unter dem Zeiger (0: keiner).
Beispiel
let under = ui.button(at: x, y)   // 0: kein Knopf
aus sdk-apps/greeter (gekürzt)
var pressed / var focusedGedrückter bzw. fokussierter Knopf.
mutating button(_ canvas: Canvas, _ rect: Rect, id: Int, _ title: TextLine, kind: ButtonKind = .secondary, size: ButtonSize = .regular, symbol: ButtonSymbol? = nil, enabled: Bool = true, background: RGB = UITheme.background)Einen Knopf direkt zeichnen und eintragen (ohne Views).
Beispiel
ui.button(canvas, plus, id: Buttons.louder, louder, kind: .secondary)
aus apps/settings (gekürzt)
mutating beginFrame() / addButton(_ id: Int, _ rect: Rect)Neues Bild beginnen / eigenen Bereich als Knopf eintragen.
Tastatur-Navigation
mutating key(_ press: Keyboard.Result, shift: Bool) -> IntTab bewegt den Fokus, Enter/Leertaste drückt; gibt die id des gedrückten Knopfs.
Beispiel
let press = keyboard.handle(code: code, down: down)
let pressed = ui.key(press, shift: keyboard.shift)
if pressed == Buttons.count { clicks += 1 }
Muster (kurz, nicht aus einem Programm kopiert)

UITheme · ButtonLook · FileLookDie Farben und der Knopf-Look des Systems

AufrufWas er tutShape-Aufruf · Zeitbudget
Farben
static var background, ground, raised, line, text, muted, faint, accent, accentPressed, onAccent, good, warning, danger, selection, selectionText, chartSystemfarben (dunkel oder hell, je nach Einstellung des Besitzers).
static var codeKeyword, codeValue, codeComment, codeShapeFarben für Quelltext (Develop).
static func use(light: Bool) / static var isLight: BoolHelle oder dunkle Palette wählen / abfragen.
Beispiel
UITheme.use(light: true)   // helle Palette; danach neu zeichnen
Muster (kurz, nicht aus einem Programm kopiert)
static let dark / static let light (UIDesign)Die beiden Paletten als UIDesign-Werte.
Knopf-Look
ButtonLook.draw(_ canvas: Canvas, _ rect: Rect, _ kind: ButtonKind, _ size: ButtonSize, title: TextLine? = nil, symbol: ButtonSymbol? = nil, state: ButtonState = ButtonState(), background: RGB = UITheme.background)Knopf im Systemlook zeichnen (Varianten nach ButtonKind, Größen nach ButtonSize).
ButtonLook.width(_ title: TextLine, _ size: ButtonSize) -> IntBreite eines Knopfs.
ButtonLook.drawSymbol / roundedFill / coloursSymbol, gerundete Fläche, Farben eines Zustands.
Dateifarben
FileLook.colour(_ name: Span<UInt8>) -> RGBFarbe, mit der Files einen Dateityp zeigt.

KeyboardTastencodes zu Zeichen

AufrufWas er tutShape-Aufruf · Zeitbudget
Tasten auswerten
init()Zustand der Umschalttasten.
mutating handle(code: UInt8, down: Bool) -> ResultWertet ein .key-Ereignis aus: .character, .text, .special(…) oder .none.
Beispiel
let press = keyboard.handle(code: code, down: down)
switch press {
case .special(.down): scrollY += 40
case .character(0x20): scrollY += area.height - 40
default: break
}
aus apps/reader (gekürzt)
static utf8(_ code: UInt32, into out: inout InlineArray<4, UInt8>) -> IntEin Zeichen als UTF-8.
Umschalttasten
private(set) var shift, control, option, command, altGrGedrückte Umschalttasten.

InputFieldEin Eingabefeld mit Auswahl, Undo und Zwischenablage

AufrufWas er tutShape-Aufruf · Zeitbudget
Anlegen
init(maximumLength: Int = 512, secret: Bool = false)Feld anlegen; secret zeigt Punkte (Passwörter).
Beispiel
nonisolated(unsafe) var passwordInput = InputField(maximumLength: 127, secret: true)
passwordInput.placeholder = TextLine("Password")
aus apps/reader (gekürzt)
Eingabe weitergeben
mutating key(_ press: Keyboard.Result, _ keyboard: Keyboard) -> ChangeTaste, solange das Feld den Fokus hat; Ergebnis .none/.moved/.edited/.submit/.cancel.
Beispiel
switch passwordInput.key(press, keyboard) {
case .submit: submitPassword()
case .none: return false
default: break
}
aus apps/reader (gekürzt)
mutating command(_ id: Int) -> ChangeBefehl des Bearbeiten-Menüs (EditMenu-ids) ausführen.
Beispiel
if searchFocused { let change = searchInput.command(id); if change == .edited { search() } }
aus apps/reader (gekürzt)
mutating pointer(_ x: Int, _ y: Int, down: Bool, pressed: Bool) -> BoolZeiger-Ereignis (Klick, Ziehen); true wenn das Feld es nahm.
Beispiel
if editing && queryInput.pointer(x, y, down: down, pressed: pressed) { surface.setPointer(.text); dirty = true }
aus apps/database (gekürzt)
Zeichnen
mutating draw(_ canvas: Canvas, _ rect: Rect, focused: Bool)Feld zeichnen: Rahmen, Text, Auswahl, Cursor.
Beispiel
searchInput.draw(canvas, field, focused: searchFocused)
aus apps/reader (gekürzt)
contains(_ x: Int, _ y: Int) -> BoolLiegt der Zeiger über dem Feld (für den Text-Zeiger)?
Inhalt setzen & lesen
mutating set(_ text: Span<UInt8> | TextLine) / clear()Text ersetzen / leeren.
mutating select(from start: Int, to end: Int)Cursor und Auswahl setzen.
withBytes<R>(_ body: (Span<UInt8>) -> R) -> R / var line: TextLineDen Inhalt lesen (Bytes oder erste 160 Bytes als Zeile).
Beispiel
var password: [UInt8] = []
passwordInput.withBytes { for byte in $0 { password.append(byte) } }
aus apps/reader (gekürzt)
Eigenschaften
var placeholder, bordered, secret, face, textColour, cursorColour, maximumLengthAussehen und Verhalten.
var isEmpty, hasSelection / private(set) var cursor, anchor, count, frameZustand des Felds.

Rect · Size · RGBGrundtypen

AufrufWas er tutShape-Aufruf · Zeitbudget
Grundtypen
Rect(x: Int, y: Int, width: Int, height: Int) / right / bottomEin Rechteck in Fensterpunkten.
Size(width: Int, height: Int)Eine Größe (Views messen damit).
RGB(_ red: UInt8, _ green: UInt8, _ blue: UInt8) / mixed(with other: RGB, _ amount: UInt32) -> RGBEine Farbe; Mischung zweier Farben.
Canvas.physicalPixel(_:_:) / setPhysicalPixel(_:_:_:) / shiftsPixel im physischen Raster (ohne scale) und Farbformat des Bildschirms.

ScrollBar · ListBar · Chart · DamageTrackerBausteine

AufrufWas er tutShape-Aufruf · Zeitbudget
Rollbalken
ScrollBar.draw(_ canvas: Canvas, _ rect: Rect, total: Int, visible: Int, offset: Int, active: Bool = false)Rollbalken im Systemlook (Variante B).
Beispiel
ScrollBar.draw(canvas, listBar, total: entryCount, visible: visibleRows, offset: scroll, active: listBarGrab != nil)
aus apps/files (gekürzt)
ScrollBar.needed / thumb / part(at:…) / offset(after:…) / offset(dragTo:…)Braucht es einen Balken, wo liegt der Griff, Klick und Ziehen auswerten.
ListBar.place(_ area: Rect, total: Int, visible: Int) -> Int / pointer(…) / draw(_:offset:)Rollbalken für Listen mit eigenem Zustand.
Diagramme
Chart.frame(_ canvas: Canvas, _ rect: Rect, low: Double, high: Double, lines: Int, labelWidth: Int, …, label: (Double) -> TextLine) -> PlotDiagrammrahmen mit Gitter und Beschriftung; liefert die Zeichenfläche.
Beispiel
let plot = Chart.frame(canvas, panel, low: 0, high: Double(top), lines: 5, labelWidth: 48, rightRoom: 58) { value in
    var line = TextLine(); line.append(decimal: UInt64(value)); return line
}
aus apps/activity (gekürzt)
Chart.series(_ canvas: Canvas, _ plot: Plot, count: Int, slots: Int, colour: RGB, area: Bool, value: (Int) -> Double)Eine Messreihe als Linie oder Fläche.
Beispiel
Chart.series(canvas, plot, count: values.count, slots: historyLength, colour: Chart.cpu, area: true) { index in
    values[index]
}
aus apps/activity (gekürzt)
Chart.cursor / endPoint / dot / niceTop / roundedPanelCursor, Endpunkt mit Wert, Punkt, runde Obergrenze, Panel.
Plot(area: Rect, low: Double, high: Double): x(of:count:slots:) / y(_:) / index(at:count:slots:)Umrechnung Wert ↔ Pixel.
Geänderte Kacheln
DamageTracker.changes(_ canvas: Canvas, handle: UInt64, _ changed: (Rect) -> Void)Findet geänderte Kacheln (für presentChanges).

ScreenNur der Display-Dienst (display.own)

AufrufWas er tutShape-Aufruf · Zeitbudget
Bildschirm
static open() -> (canvas: Canvas, needsFlush: Bool)?Übernimmt den Bildschirm.≤ 5 msdisplay.open0x0011_0001
Beispiel
guard let (screen, needsFlush) = Screen.open() else { return }   // nur mit display.own
aus apps/display (gekürzt)
static flush(_ rect: Rect)Zeigt ein geändertes Rechteck (virtio-gpu).≤ 200 µsdisplay.flush0x0011_0002
static saveScreenshot() -> BoolSpeichert den Bildschirm als PNG in /data/screenshots.≤ 50 msdisplay.screenshot0x0011_0003
Fenster & Eingabe
static surface(_ index: Int) -> SurfaceInfoFenster Nummer index aus Sicht des Dienstes.≤ 20 µsdisplay.surface0x0011_0005
static input(into events: inout InlineArray<96, UInt64>) -> IntEingabe-Ereignisse abholen.inputReceive (Systemaufruf 12)
static wait(timeout: UInt64) / notify(_ index: Int)Auf Programme warten / ein Fenster wecken.wait / surfaceNotify
Beispiel: Ein Fenster mit Menü, Knopf und Ereignisschleife (sdk-apps/greeter)
nonisolated(unsafe) var ui = UI()
nonisolated(unsafe) var clicks = 0

enum Buttons { static let count = 1 }

@c(app_main)
func appMain() {
    guard var surface = Surface.create() else { System.log("greeter: no window"); return }
    surface.requestSize(width: 360, height: 220)
    surface.addMenu(0, "Counter")
    surface.addItem(0, "Count", id: Buttons.count, key: 0x6E) // Cmd+N
    surface.publishMenus()
    var dirty = true
    while true {
        while let event = surface.nextEvent() {
            switch event {
            case .configure: dirty = true
            case .close: System.exit(0)
            case .menu(let id): if id == Buttons.count { clicks += 1; dirty = true }
            case .pointer(let x, let y, let buttons):
                let under = ui.button(at: x, y)
                surface.setPointer(under != 0 ? .hand : .arrow)
                if buttons & 2 != 0 {
                    if ui.pressed == 0 { ui.pressed = under; dirty = true }
                } else if ui.pressed != 0 {
                    if under == ui.pressed && under == Buttons.count { clicks += 1 }
                    ui.pressed = 0
                    dirty = true
                }
            default: break
            }
        }
        if dirty && surface.canvas.width > 0 {
            draw(surface.canvas)
            surface.present(Rect(x: 0, y: 0, width: surface.canvas.width, height: surface.canvas.height))
            dirty = false
        }
        surface.wait(timeout: 500_000_000)
    }
}
Beispiel: Inhalt mit Views beschreiben (sdk-apps/greeter)
private func draw(_ canvas: Canvas) {
    var text = TextLine("Clicked ")
    text.append(decimal: UInt64(clicks))
    text.append(clicks == 1 ? " time" : " times")
    let view = Padded(20) {
        VStack(spacing: 14) {
            Text("greeter", face: .heading)
            Text(text, face: .body, color: UITheme.muted)
            Button("Count", id: Buttons.count)
            Spacer()
        }
    }
    canvas.render(view, in: Rect(x: 0, y: 0, width: canvas.width, height: canvas.height), &ui)
}
Beispiel: Tastatur und Suchfeld (apps/reader)
nonisolated(unsafe) var keyboard = Keyboard()
nonisolated(unsafe) var searchInput = InputField(maximumLength: 120)

// beim Start
searchInput.placeholder = TextLine("Search")

// bei case .key(let code, let down):
let press = keyboard.handle(code: code, down: down)
if searchFocused {
    switch searchInput.key(press, keyboard) {
    case .edited: search(); return true
    case .submit: return step(keyboard.shift ? -1 : 1, canvas)
    case .cancel: searchFocused = false; return true
    case .moved: return true
    case .none: break
    }
}

// beim Zeichnen
searchInput.draw(canvas, field, focused: searchFocused)
Beispiel: Nur geänderte Teile zeigen (apps/reader)
if dirty && surface.canvas.width > 0 {
    surface.beginDrawing()      // der Dienst kopiert jetzt nicht mitten ins Bild
    draw(surface.canvas)
    surface.presentChanges()    // nur die geänderten Kacheln
    dirty = false
}
surface.wait(timeout: 500_000_000)

Fortschritt

Neu (10.11.). Ein Programm sagt, was es tut und wie weit es ist („wie viel von wie viel“); das System zeichnet es überall gleich: eine Kapsel im Fenstertitel mit Balken, Prozent und Restzeit, ein dünner Balken auf der Dock-Kachel; ein Klick zeigt Details und – wenn erlaubt – Abbrechen. Buddy meldet, wenn eine lange Aufgabe fertig ist, während das Fenster nicht vorn ist. Höchstens 4 Aufgaben pro Fenster; Meldungen gehen höchstens 10-mal pro Sekunde raus. Kein eigenes Recht nötig (das Fenster genügt, ui.surface).

Surface (Fortschritt)

AufrufWas er tutShape-Aufruf · Zeitbudget
Aufgabe starten
progress(_ title: TextLine, total: UInt64, unit: ProgressUnit = .none, cancellable: Bool = false) -> ProgressTask? neuStartet eine Aufgabe im Titel und im Dock; total 0 = unbekannt (laufender Streifen). Nil bei schon 4 Aufgaben.surfaceNotify (Systemaufruf 11)
Beispiel
guard let task = surface.progress(TextLine("Export 1200 rows"), total: 1200, unit: .items, cancellable: true) else { return }
Muster (kurz, nicht aus einem Programm kopiert)
Stand melden
progress(_ task: ProgressTask, done: UInt64) neuWie weit die Aufgabe ist (gedrosselt auf 10/s).
Beispiel
surface?.progress(running, done: UInt64(response.body.count))
aus apps/web (gekürzt)
Beenden
progress(_ task: ProgressTask, finished end: ProgressEnd, done: UInt64? = nil) neuAufgabe beendet; bleibt kurz grün (oder rot) im Titel.
Beispiel
surface?.progress(running, finished: cancelled ? .cancelled : result.problem == nil ? .done : .failed)
aus apps/web (gekürzt)
Abbrechen erkennen
isCancelled(_ task: ProgressTask) -> Bool neuHat die Person Abbrechen gedrückt? Jederzeit fragen, auch mitten in einer Schleife.
Beispiel
if surface?.isCancelled(running) == true { cancelled = true; break }
aus apps/web (gekürzt)
static let cancelRequests neuByte-Offset der Abbruch-Bits im Ringblatt (ein Bit pro Aufgabe).

Typen

AufrufWas er tutShape-Aufruf · Zeitbudget
Typen
enum ProgressUnit: UInt8 { none, bytes, items } neuWas done und total zählen.
enum ProgressEnd: UInt8 { done, failed, cancelled } neuWie eine Aufgabe endete.
struct ProgressTask { let id: Int } neuEine laufende Aufgabe des Fensters.
SurfaceMessage.progress neuNachrichtenart 12 (Programm → Dienst).
Beispiel: Download mit Fortschritt und Abbrechen (apps/web, Fetch.swift, gekürzt)
// Lange Antworten zeigen ihren Fortschritt (das System zeichnet ihn).
if task == nil {
    task = surface?.progress(TextLine("Download Big_Buck_Bunny.webm"),
                             total: UInt64(response.expectedLength ?? 0), unit: .bytes, cancellable: true)
}
if let running = task {
    surface?.progress(running, done: UInt64(response.body.count))
    if surface?.isCancelled(running) == true {
        cancelled = true
        break
    }
}

// am Ende (defer):
if let running = task {
    surface?.progress(running, finished: cancelled ? .cancelled : result.problem == nil ? .done : .failed)
    task = nil
}

Dateien & Einstellungen

Mit files.own beginnt jeder Pfad im eigenen Ordner /data/<id> – sonst existiert nichts. files.system sieht das ganze Volume (/apps nur lesend, system/…, in der VM mac/…); files.mac ergänzt den eigenen Ordner um den Mac-Ordner. Pfade mit „/“, „.“ und „..“ werden abgelehnt. Änderungen gelten, wenn die Datei geschlossen wird; pro Systemaufruf höchstens 16 KiB. Ein Programm mit opens = … bekommt sein übergebenes Dokument mit File.openDocument ohne Datei-Recht, eine im Dialog gewählte Datei mit File.open(grant, mode:).

Settings sind kleine, private Einstellungen pro Programm (key = value, höchstens 4 KiB), vom System in DATA gehalten – kein Datei-Recht nötig.

Hinweis: File, Files und FileError liegen in userland/shapes/ und stehen (noch) nicht in app-api-1.txt – tools/api/symbols.py liest diesen Ordner nicht. Sie gehören trotzdem zur App-API (README, Abschnitt Files).

FileEine geöffnete Datei

AufrufWas er tutShape-Aufruf · Zeitbudget
Öffnen
static open(_ path: Span<UInt8> | StaticString, mode: Mode) -> Result<File, FileError>Öffnet eine Datei; Modus .read, .replace (anlegen/leeren), .append, .update (lesen und schreiben an jeder Stelle).≤ 2 msfiles.open0x0003_0001
Beispiel
guard case .success(let file) = File.open("notes.txt", mode: .read) else { return }
aus sdk-apps/notes (gekürzt)
static openDocument(mode: Mode) -> Result<File, FileError>Öffnet das von Files übergebene Dokument (siehe Apps.document).≤ 2 msfiles.open0x0003_0001
Beispiel
switch File.openDocument(mode: .read) {
case .failure(let error): message = TextLine(error.text)
case .success(let file): read(file)
}
aus apps/reader (gekürzt)
static open(_ grant: FileGrant, mode: Mode) -> Result<File, FileError> neuÖffnet eine im Datei-Dialog gewählte Datei.≤ 2 msfiles.open0x0003_0001
Lesen
read(into buffer: inout MutableSpan<UInt8>) -> Result<Int, FileError>Liest die nächsten Bytes (höchstens 16 KiB); 0 am Ende.≤ 2 msfiles.read0x0003_0002
Beispiel
var chunk = InlineArray<1024, UInt8>(repeating: 0)
var span = chunk.mutableSpan
guard case .success(let count) = file.read(into: &span), count > 0 else { break }
aus sdk-apps/notes (gekürzt)
read(into buffer: inout MutableSpan<UInt8>, at offset: UInt64) -> Result<Int, FileError>Liest ab einer beliebigen Stelle.≤ 2 msfiles.read0x0003_0002
Beispiel
guard case .success(let got) = file.read(into: &span, at: 0), got >= 44 else { return }   // WAV-Kopf
aus apps/sound (gekürzt)
Schreiben
write(_ data: Span<UInt8>) -> FileError?Hängt alle Bytes an (in Stücken zu 16 KiB).≤ 2 msfiles.write0x0003_0003
Beispiel
bytes.withUnsafeBufferPointer { buffer in _ = file.write(Span(_unsafeElements: buffer)) }
aus sdk-apps/notes (gekürzt)
write(_ data: Span<UInt8>, at offset: UInt64) -> FileError?Schreibt an eine Stelle (höchstens bis zur Größe: keine Löcher).≤ 2 msfiles.write0x0003_0003
Beispiel
_ = pending.withUnsafeBufferPointer { handle.write(Span(_unsafeElements: $0), at: bytes) }
aus apps/sound (gekürzt)
Schließen
close() -> FileError?Schließt und übernimmt die Änderungen.≤ 5 msfiles.close0x0003_0004
Beispiel
_ = file.close()   // erst jetzt sind die Änderungen übernommen
aus sdk-apps/about (gekürzt)
Konstanten
static let chunk = 16384 / let handle: UInt64Bytes pro Systemaufruf / Nummer der Datei.

FilesOrdner, Infos und Änderungen über Pfade

AufrufWas er tutShape-Aufruf · Zeitbudget
Ordner
static entry(in path: Span<UInt8>, at index: Int, snapshot: UInt64? = nil) -> Result<Entry?, FileError>Der index-te Eintrag eines Ordners (optional wie in einem Snapshot); nil nach dem letzten. Index 0 liest neu.≤ 2 msfiles.list0x0003_0005
Beispiel
var index = 0
while case .success(let entry?) = folder.withUnsafeBufferPointer({ Files.entry(in: Span(_unsafeElements: $0), at: index) }) {
    index += 1
}
aus apps/testcamera (gekürzt)
static makeDirectory(_ path: Span<UInt8>) -> FileError?Ordner anlegen (leerer Pfad: der eigene Ordner).≤ 2 msfiles.makeFolder0x0003_0007
Beispiel
_ = folder.withUTF8Buffer { Files.makeDirectory(Span(_unsafeElements: $0)) }
aus apps/sound (gekürzt)
struct Entry { isDirectory, size, modified, name, nameLength }Ein Ordnereintrag.
Info
static info(_ path: Span<UInt8> | StaticString, snapshot: UInt64? = nil) -> Result<(isDirectory: Bool, size: UInt64, modified: UInt64), FileError>Ordner?, Größe (Ordner: Gesamtgröße), letzte Änderung.≤ 200 µsfiles.info0x0003_0006
Beispiel
guard case .success(let runs) = Files.info("runs.log") else { return fail("runs.log has no size") }
aus apps/shapecheck (gekürzt)
static volume(system: Bool = false) -> InlineArray<5, UInt64>?DATA oder Systemvolume: Blockgröße, Blöcke, belegt, Snapshots, Generation.≤ 50 µsfiles.volume0x0003_000B
Beispiel
if let data = Files.volume() { dataUsed = data[2] * data[0]; dataTotal = data[1] * data[0]; snapshots = data[3] }
aus apps/activity (gekürzt)
Ändern
static remove(_ path: Span<UInt8> | StaticString) -> FileError?Datei oder leeren Ordner löschen.≤ 2 msfiles.remove0x0003_0008
Beispiel
if Files.remove("shapecheck-moved.txt") != nil { failed("remove") }
aus apps/shapecheck (gekürzt)
static rename(_ path: Span<UInt8>, to newPath: Span<UInt8>, replacing: Bool) -> FileError?Verschieben/umbenennen; mit replacing atomar über eine vorhandene Datei.≤ 2 msfiles.rename0x0003_0009
Beispiel
let renamed = old.withUTF8Buffer { o in new.withUTF8Buffer { n in Files.rename(Span(_unsafeElements: o), to: Span(_unsafeElements: n)) } }
aus apps/sharecheck (gekürzt)
static replace(_ path: Span<UInt8>, with data: Span<UInt8>) -> FileError?Ganzen Inhalt atomar ersetzen (erst .new, dann umbenennen).≤ 2 msfiles.open + files.rename0x0003_0001
Beispiel
_ = path.withUnsafeBufferPointer { p in bytes.withUnsafeBufferPointer { b in Files.replace(Span(_unsafeElements: p), with: Span(_unsafeElements: b)) } }
aus apps/settings (gekürzt)
Zeigen
static reveal(_ path: Span<UInt8>) -> FileError?Zeigt eine Datei des eigenen Ordners in Files (Web-Downloads; braucht apps.launch).ohne Budgetfiles.reveal0x0003_000A
Beispiel
_ = name.withUnsafeBufferPointer { Files.reveal(Span(_unsafeElements: $0)) }   // z. B. "downloads/x.pdf"
Muster (kurz, nicht aus einem Programm kopiert)

Files (Snapshots, files.system)Der Zeitschieber

AufrufWas er tutShape-Aufruf · Zeitbudget
Schnappschüsse
static snapshot(after previous: UInt64) -> Snapshot?Der Snapshot nach previous (0: der älteste).≤ 50 µsfiles.snapshot0x0003_000C
Beispiel
while let next = Files.snapshot(after: previous) { list.append(next); previous = next.id }
aus apps/files (gekürzt)
static takeSnapshot() -> Result<UInt64, FileError>Jetzt einen Snapshot nehmen (bleibt bis gelöscht).≤ 50 msfiles.takeSnapshot0x0003_000D
Beispiel
if case .success(let id) = Files.takeSnapshot() { kept = id }
Muster (kurz, nicht aus einem Programm kopiert)
static deleteSnapshot(_ id: UInt64) -> FileError?Snapshot löschen und nur von ihm belegten Platz freigeben.≤ 50 msfiles.deleteSnapshot0x0003_000E
Beispiel
if next.kind == .requested { _ = Files.deleteSnapshot(next.id) }
aus apps/slidercheck (gekürzt)
Wiederherstellen
static restore(_ path: Span<UInt8>, snapshot: UInt64) -> Result<(name: InlineArray<80, UInt8>, length: Int), FileError>Datei/Ordner aus einem Snapshot zurückkopieren; nichts wird überschrieben ("name (HH.MM).ext").≤ 2 sfiles.restore0x0003_000F
Beispiel
switch Files.restore(path.span.extracting(0..<length), snapshot: snapshots[shown].id) {
case .success(let restored): note(restored.name, restored.length)
case .failure(let error): problem = TextLine(error.text)
}
aus apps/files (gekürzt)

FileError

AufrufWas er tutShape-Aufruf · Zeitbudget
FileError
refused, badArgument, notFound, exists, notADirectory, notEmpty, noSpace, ioError, isADirectory, tooManyOpen, inUse, locked, wrongContentWarum eine Dateioperation scheiterte; locked = DATA noch versiegelt, wrongContent = Inhalt passt nicht zur Endung.
var text: StaticString / static from(_ result: UInt64) -> FileError?Kurzer englischer Satz für Meldungen / Fehler aus einem Rückgabewert.

FileTypeDokumenttypen

AufrufWas er tutShape-Aufruf · Zeitbudget
Dokumenttypen
static of(_ name: Span<UInt8>) -> FileTypeTyp nach der Endung.
var label: StaticString / isText / isDatabaseAnzeigename und Art.
accepts(_ prefix: Span<UInt8>, complete: Bool) -> BoolPasst der Inhaltsanfang zum Typ?
static extensionRange / validUTF8 / namesSVGElement / equal / maxExtensionHilfen zu Endungen und Inhalt.

SettingsEigene Einstellungen (ohne Datei-Recht)

AufrufWas er tutShape-Aufruf · Zeitbudget
Laden
static load() -> Settings?Die zuletzt gesicherten Einstellungen (beim ersten Mal leer); nil solange DATA gesperrt ist und für Echtzeit-Programme.≤ 200 µsprograms.settings0x0006_0002
Beispiel
guard var settings = Settings.load() else { fail("settings could not be loaded"); return }
aus apps/apicheck (gekürzt)
Lesen
value(_ key: StaticString) -> TextLine?Wert eines Schlüssels.
Beispiel
let greeting = settings.value("greeting")   // TextLine?
aus apps/apicheck (gekürzt)
number(_ key: StaticString) -> Int?Wert als ganze Zahl.
Beispiel
let runs = (settings.number("runs") ?? 0) + 1
aus apps/apicheck (gekürzt)
Setzen
mutating set(_ key: StaticString, _ value: TextLine) -> BoolSetzt einen Text; false wenn 4 KiB überschritten würden.
Beispiel
settings.set("greeting", TextLine("hello robot"))
aus apps/apicheck (gekürzt)
mutating set(_ key: StaticString, number: Int) -> BoolSetzt eine Zahl.
Beispiel
settings.set("runs", number: runs)
aus apps/apicheck (gekürzt)
Sichern
save() -> BoolSchreibt die Einstellungen.≤ 2 msprograms.saveSettings0x0006_0003
Beispiel
guard settings.save() else { fail("settings were not kept"); return }
aus apps/apicheck (gekürzt)
Beispiel: Eigene Datei lesen und schreiben (sdk-apps/notes)
// manifest: capabilities = system.log, ui.surface, files.own   (+ uses-heap)
guard case .success(let file) = File.open("notes.txt", mode: .read) else { return }
var bytes: [UInt8] = []
var chunk = InlineArray<1024, UInt8>(repeating: 0)
while true {
    var span = chunk.mutableSpan
    guard case .success(let count) = file.read(into: &span), count > 0 else { break }
    for index in 0..<count { bytes.append(chunk[index]) }
}
file.close()

guard case .success(let out) = File.open("notes.txt", mode: .replace) else { return }
bytes.withUnsafeBufferPointer { buffer in _ = out.write(Span(_unsafeElements: buffer)) }
out.close()
Beispiel: Einen Ordner auflisten (apps/testcamera)
var index = 0
while case .success(let entry?) = folder.withUnsafeBufferPointer({ Files.entry(in: Span(_unsafeElements: $0), at: index) }) {
    // entry.name[0..<entry.nameLength], entry.isDirectory, entry.size, entry.modified
    index += 1
}
Beispiel: Das übergebene Dokument öffnen (apps/reader)
// manifest: opens = pdf   – kein files-Recht nötig
guard let document = Apps.document(), document.generation != generation else { return false }
generation = document.generation
switch File.openDocument(mode: .read) {
case .failure(let error): message = TextLine(error.text)
case .success(let file):  // ... lesen, file.close()
}
Beispiel: Einstellungen merken (docs/api/README.md)
if var settings = Settings.load() {
    let starts = (settings.number("starts") ?? 0) + 1
    settings.set("starts", number: starts)
    settings.set("greeting", TextLine("hello robot"))
    _ = settings.save()
}

Datenbank (SOFTrobotsDB)

Tabellen mit typisierten Feldern, gehalten und geprüft vom Kern (Modul db). Ein Programm beschreibt seine Tabellen als gespeicherte Shapes in data.shape; shapec --database erzeugt daraus Swift-Code (openDatabase, je Tabelle ein Typ mit insert, update, delete, set…, row und den Feldern). Regeln wie unique, Bereiche und Verweise prüft der Kern bei jedem Schreiben; ein Schreibsatz gilt ganz oder gar nicht. Kein Heap: Schreibsätze, Abfragen und Seiten halten ihre Bytes inline (als globale Variablen anlegen). Die eigenen Datenbanken liegen im eigenen Ordner (files.own); fremde schreiben nur mit database.edit.

Wie Dateien liegt die Datenbank-Bibliothek in userland/shapes/ und steht nicht in app-api-1.txt.

Database

AufrufWas er tutShape-Aufruf · Zeitbudget
Öffnen & schließen
openDatabase(_ path: StaticString) -> Result<Database, DatabaseError>Vom Generator erzeugt: öffnet/legt an und gleicht die Tabellen ans Schema an (Felder hinzu, umbenannt, weg – ohne Zeilen umzuschreiben).≤ 2 msdb.open0x0004_0001
Beispiel
guard case .success(let robot) = openDatabase("robot.db") else { return fail("cannot open robot.db") }
aus apps/dbcheck (gekürzt)
static open(_ path: StaticString, schema: Span<UInt8>) -> Result<Database, DatabaseError>Dasselbe mit einem kompilierten Schema.≤ 2 msdb.open0x0004_0001
static open(_ path: Span<UInt8>) -> Result<Database, DatabaseError>Öffnet, wie sie ist (Werkzeuge wie Konsole und Database).≤ 2 msdb.open0x0004_0001
Beispiel
guard case .success(let database) = path.withSpan({ Database.open($0) }) else { return false }
aus apps/database (gekürzt)
close()Schließen.≤ 50 µsdb.close0x0004_0005
Beispiel
robot.close()
aus apps/dbcheck (gekürzt)
Schreiben
write<let capacity: Int>(_ set: inout WriteSet<capacity>, soon: Bool = false) -> Result<Int, DatabaseError>Wendet einen Schreibsatz an (alles oder nichts); gibt die Zahl eingefügter Zeilen. soon: kehrt sofort zurück, auf der Platte binnen 10 ms.≤ 1 msdb.write0x0004_0002
Beispiel
Joints.update(&set, id: 7)
Joints.setAngle(&set, 12_345)
guard case .success = robot.write(&set) else { return fail("updating joint 7 failed") }
aus apps/dbcheck (gekürzt)
Abfragen
next(_ query: inout Query<q>, into page: inout QueryPage<output, cursor>) -> BoolFührt eine Abfrage aus bzw. holt die nächste Seite; false wenn fertig oder Fehler (page.error).≤ 5 msdb.query0x0004_0003
Beispiel
page.reset()
while robot.next(&query, into: &page) || page.rowCount > 0 {
    for row in 0..<page.rowCount { let joint = Joints.row(&page, row); _ = joint.angle }
    if page.rowCount == 0 { break }
}
aus apps/dbcheck (gekürzt)
next(query: Span<UInt8>, aggregate: Bool, grouped: Bool, into page: …) -> BoolDasselbe für eine Abfrage aus SQL.translate (uses-sql).≤ 5 msdb.query0x0004_0003
firstValue<R>(query: Span<UInt8>, _ body: (Span<UInt8>?) -> R) -> RErster Wert einer übersetzten Abfrage.≤ 5 msdb.query0x0004_0003
Beschreibung
describe(into buffer: inout MutableSpan<UInt8>) -> Result<Int, DatabaseError>Tabellen, Felder und Indizes.≤ 1 msdb.describe0x0004_0004

WriteSet<capacity>Ein Schreibsatz

AufrufWas er tutShape-Aufruf · Zeitbudget
Satz anlegen
init() / clear() / var isEmptyLeerer Satz.
Operationen
insert(_ table: StaticString, id: UInt64 = 0) / update(_:id:) / delete(_:id:)Operation beginnen (der erzeugte Code ruft das für dich).
set(_ field: Int, bytes: | bool: | int:size: | unsigned:size: | float: | double: | text:) / setNull(_:)Feldwerte der aktuellen Operation.
Ergebnis
internal(set) var insertedIDs / insertedCountids der eingefügten Zeilen.

Query<capacity>Eine Abfrage

AufrufWas er tutShape-Aufruf · Zeitbudget
Tabelle
init(_ table: StaticString)Abfrage auf einer Tabelle.
Beispiel
query = Query(Joints.table)
aus apps/dbcheck (gekürzt)
Filtern
where(_ condition: Condition) / whereAny(_:_:_:_:)Bedingung (UND) / bis zu vier ODER-Bedingungen.
Beispiel
query.where(Joints.angle >= -10_000)
query.where(Joints.angle < 10_000)
aus apps/dbcheck (gekürzt)
Sortieren & begrenzen
sorted(by field: Int, descending: Bool = false) / sorted(by:via:descending:)Sortieren (auch über einen Verweis, Join).
Beispiel
query.sorted(by: Joints.angle.index, descending: true)
aus apps/dbcheck (gekürzt)
limit(_ rows: Int) / select(_ field: Int)Höchstens n Zeilen / nur bestimmte Felder.
Beispiel
query.limit(10)
aus apps/dbcheck (gekürzt)
Aggregate
count(groupedBy:) / sum / minimum / maximum / average (_ field: Int, groupedBy: Int? = nil)Aggregate, optional gruppiert.
Beispiel
query.count(groupedBy: Joints.status.index)
// je Gruppe: page.aggregate(row).rows
aus apps/dbcheck (gekürzt)

Felder & Bedingungen

AufrufWas er tutShape-Aufruf · Zeitbudget
Felder & Bedingungen
NumberField: == != < <= > >= Int64, isNull, isNotNull, via(_ ref:)Bedingungen auf Zahlenfeldern; via = Feld der verwiesenen Tabelle (Join, höchstens zwei Verweise).
Beispiel
query.where(Joints.status.via(Moves.joint) == 1)   // Join über den Verweis
aus apps/dbcheck (gekürzt)
FloatField: == != < <= > >= DoubleBedingungen auf Gleitkommafeldern.
TextField: == StaticString, equals, startsWith, contains, before, afterBedingungen auf Textfeldern.

QueryPage<output, cursor>Eine Ergebnisseite

AufrufWas er tutShape-Aufruf · Zeitbudget
Seite
init() / reset()Seite anlegen / für eine neue Abfrage leeren.
rowCount, error, plan, indexZeilen, Fehler, gewählter Plan (Index, Bereich …).
Werte lesen
id(_ row: Int) / number(_:_:signed:) / float / double / text / withBytes / isNull / valueWerte einer Zeile lesen.
aggregate(_ row: Int) -> (rows: UInt64, value: Int64?) / groupNumber / withGroupBytesErgebnis eines Aggregats bzw. einer Gruppe.
Beispiel
for row in 0..<page.rowCount { total += page.aggregate(row).rows }
aus apps/dbcheck (gekürzt)

Series & Fehler

AufrufWas er tutShape-Aufruf · Zeitbudget
Messreihen
Series.info(_ path: Span<UInt8>) -> Result<Info, FileError>Was eine Messreihe hält: Satzgröße, Sätze, erste/letzte Zeit.≤ 5 msdb.readSeries0x0004_0006
Series.read(_ path: Span<UInt8>, from: UInt64, to: UInt64, …)Sätze eines Zeitbereichs (Flugschreiber, Recorder).≤ 5 msdb.readSeries0x0004_0006
Fehler
DatabaseError: .system(FileError), .rule(DatabaseRule, operation:, field:)Systemfehler oder verletzte Regel (duplicate, outOfRange, badReference …).
Beispiel
guard case .failure(.rule(.duplicate, operation: 0, field: 1)) = robot.write(&set) else { return fail("a duplicate name was accepted") }
aus apps/dbcheck (gekürzt)
Beispiel: Tabellen beschreiben (apps/dbcheck/data.shape)
enum JointStatus { ok @0, warning @1, fault @2 }

shape Joint stored as joints {
    name: Text(32) unique
    angle: Int32 in -180000 ... 180000
    current: Float32?
    status: JointStatus = .ok
    index status, angle
    index angle
}

shape Move stored as moves {
    joint: Joint
    at: Time
    target: Int32
}
Beispiel: Schreiben, Regeln, Abfrage über Index, Gruppen (apps/dbcheck)
nonisolated(unsafe) var set = WriteSet<65536>()
nonisolated(unsafe) var query = Query<512>(Joints.table)
nonisolated(unsafe) var page = QueryPage<16384, 1024>()

guard case .success(let robot) = openDatabase("robot.db") else { return fail("cannot open robot.db") }

Joints.insert(&set, name: name.span.extracting(0..<length), angle: 45_000)
Joints.setStatus(&set, .warning)
guard case .success(1) = robot.write(&set) else { return fail("insert failed") }

// Regeln prüft der Kern:
guard case .failure(.rule(.duplicate, operation: 0, field: 1)) = robot.write(&set) else { ... }

// Die zehn größten Winkel über 90°, über den Index rückwärts
query = Query(Joints.table)
query.where(Joints.angle > 90_000)
query.sorted(by: Joints.angle.index, descending: true)
query.limit(10)
page.reset()
while robot.next(&query, into: &page) || page.rowCount > 0 {
    for row in 0..<page.rowCount { let joint = Joints.row(&page, row); _ = joint.angle }
    if page.rowCount == 0 { break }
}

// Join: Bewegungen, deren Gelenk den Status warning hat
query = Query(Moves.table)
query.where(Joints.status.via(Moves.joint) == 1)
query.count()

Netzwerk & TLS

Ausgehend mit network.outgoing: TCP (IPv4/IPv6) und UDP zu genau einem Server. Eine Zeile connects = host, host begrenzt die erreichbaren Hosts; Namen löst dann nur der Kern auf. Eingehend mit network.incoming und listens = 443, 80 (nur das Programm Server; nie während eines Not-Halts) – es gibt keinen öffentlichen Webserver, das Tor liegt im Heimnetz plus WireGuard.

TLS 1.3 kommt mit uses-tls als Bibliothek (SecureConnection: Zertifikatskette gegen die Vertrauensanker des Systems, DNS über HTTPS, bis zu 4 Verbindungen). Claude mit uses-ai; der API-Schlüssel ist das Geheimnis des Programms (Settings › Privatsphäre).

SecureConnection und Claude sind Bibliotheken der Profile uses-tls/uses-ai und nicht in app-api-1.txt erfasst.

NetConnectionEine TCP-Verbindung

AufrufWas er tutShape-Aufruf · Zeitbudget
Namen auflösen
static resolve(_ name: StaticString | Span<UInt8>) -> UInt32?IPv4-Adresse eines Hostnamens (DNS des Netzes).≤ 6 snet.resolve0x0005_0003
Beispiel
guard let address = NetConnection.resolve("example.com") else { return }
Muster (kurz, nicht aus einem Programm kopiert)
static resolveIPv6(_ name: Span<UInt8>) -> InlineArray<16, UInt8>?IPv6-Adresse (AAAA).≤ 6 snet.resolve0x0005_0003
Verbinden
init?(address: UInt32, port: UInt16)Verbindung öffnen (Handshake läuft im Hintergrund).≤ 5 snet.connect0x0005_0004
Beispiel
guard let connection = NetConnection(address: address, port: 80) else { return }
Muster (kurz, nicht aus einem Programm kopiert)
init?(ipv6 address: InlineArray<16, UInt8>, port: UInt16)Über IPv6.≤ 5 snet.connect0x0005_0004
Senden & empfangen
send(_ data: Span<UInt8>) -> BoolAlle Bytes senden (wartet bei vollem Puffer).≤ 1 snet.send0x0005_0005
receive(into buffer: inout InlineArray<1024, UInt8>, timeout: UInt64 = 1_000_000_000) -> Int?Bis 1 KiB empfangen; 0 = noch nichts, nil = Gegenseite hat geschlossen.≤ 10 snet.receive0x0005_0006
Beispiel
var buffer = InlineArray<1024, UInt8>(repeating: 0)
while let count = connection.receive(into: &buffer, timeout: 2_000_000_000), count > 0 { /* buffer[0..<count] */ }
Muster (kurz, nicht aus einem Programm kopiert)
Schließen
close()Senderichtung schließen; das System gibt die Verbindung frei, wenn TCP fertig ist.≤ 50 µsnet.close0x0005_0007

DatagramSocketUDP zu einem Server

AufrufWas er tutShape-Aufruf · Zeitbudget
Öffnen
init?(address: UInt32, port: UInt16)Verbundener UDP-Socket: Datagramme von anderswo kommen nie an.≤ 5 snet.openDatagram0x0005_0008
Senden & empfangen
send(_ data: Span<UInt8>) -> BoolEin Datagramm (höchstens 1472 Bytes).≤ 1 snet.sendDatagram0x0005_0009
receive(into buffer: inout InlineArray<512, UInt8>, timeout: UInt64) -> Int?Ein Datagramm abwarten; 0 wenn keines kam.≤ 10 snet.receiveDatagram0x0005_000A
Schließen
close()Schließen.≤ 50 µsnet.closeDatagram0x0005_000B

NetListener · NetPeerEingehend (network.incoming)

AufrufWas er tutShape-Aufruf · Zeitbudget
Lauschen
init?(port: UInt16, backlog: UInt8 = 8)Lauschen; nil wenn der Port nicht im Manifest steht, belegt ist oder Not-Halt gilt.≤ 50 µsnet.listen0x0005_000C
Beispiel
guard let listener = NetListener(port: 8080, backlog: 4) else { return }
aus apps/listencheck (gekürzt)
close()Nicht mehr lauschen; angenommene Verbindungen bleiben.≤ 50 µsnet.closeListener0x0005_000F
Annehmen
accept(timeout: UInt64 = 1_000_000_000) -> (connection: NetConnection, peer: NetPeer)?Nächste Verbindung annehmen.≤ 10 snet.accept0x0005_000D
Beispiel
if let (connection, peer) = listener.accept(timeout: 1_000_000_000) {
    // peer.ipv4 / peer.port; connection.send(...), connection.close()
}
Muster (kurz, nicht aus einem Programm kopiert)
NetPeer: ipv4, ipv6, overIPv6, portWer sich verbunden hat.
Zustand
info() -> ListenerInfo?Angenommen, abgelehnt, halb offen, wartend.≤ 20 µsnet.listenerInfo0x0005_000E

SecureConnection (uses-tls)TLS 1.3

AufrufWas er tutShape-Aufruf · Zeitbudget
Verbinden
static open(_ host: StaticString, port: UInt16 = 443, alpn: StaticString = "", timeout: UInt64 = 10_000_000_000) -> SecureConnection?Auflösen (DNS über HTTPS), verbinden, Handshake mit Zertifikatsprüfung.≤ 5 snet.connect0x0005_0004
Beispiel
guard let secure = SecureConnection.open("example.com") else { return }
aus apps/https (gekürzt)
static open(host: Span<UInt8>, port: UInt16 = 443, timeout: UInt64 = 10_000_000_000) -> SecureConnection?Für einen erst zur Laufzeit bekannten Host (Browser).≤ 5 snet.connect0x0005_0004
static open(address: UInt32, serverName host: StaticString, port: UInt16 = 443, alpn: StaticString = "", timeout: UInt64 = 10_000_000_000, dns: Bool = false) -> SecureConnection?Zu einer bekannten Adresse.≤ 5 snet.connect0x0005_0004
Senden & empfangen
send(_ data: Span<UInt8>) -> BoolVerschlüsseln und senden.≤ 1 snet.send0x0005_0005
receive(into out: inout InlineArray<1024, UInt8>, timeout: UInt64 = 5_000_000_000) -> Int?Entschlüsselte Bytes; 0 = noch nichts, nil = geschlossen.≤ 10 snet.receive0x0005_0006
Beispiel
while let count = secure.receive(into: &buffer) { if count == 0 { break }; total += count }
aus apps/https (gekürzt)
Schließen
close()close_notify senden und schließen.≤ 50 µsnet.close0x0005_0007
Diagnose
static lastError / handshakeTime / handshakes / lastSlotWarum open scheiterte, Dauer des letzten Handshakes.

Claude (uses-ai)Eine Frage an die Messages API

AufrufWas er tutShape-Aufruf · Zeitbudget
Eine Frage an die Messages API
Claude.ask(_ question: String, jpeg: [UInt8]? = nil, model: String = defaultModel, maxTokens: Int = 700) -> AnswerFragt (optional mit Bild) und wartet auf die ganze Antwort (höchstens eine Minute). Kamerabilder nur, wenn der Besitzer es erlaubt hat.≤ 5 snet.connect …0x0005_0004
Beispiel
let result = Claude.ask(String(decoding: question, as: UTF8.self), jpeg: jpeg)
aus apps/ask (gekürzt)
Claude.Answer: text, problem, milliseconds, incompleteAntworttext oder Grund, Dauer.
Beispiel: HTTPS-Abruf mit TLS 1.3 (apps/https)
// manifest: capabilities = system.log, network.outgoing   (+ uses-tls)
guard let secure = SecureConnection.open("example.com") else {
    var line = TextLine("https: ")
    line.append(SecureConnection.lastError)
    System.log(line)
    return
}
let request: StaticString = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"
let sent = request.withUTF8Buffer { bytes in secure.send(Span(_unsafeElements: bytes)) }
guard sent else { System.log("https: sending failed"); return }
var buffer = InlineArray<1024, UInt8>(repeating: 0)
while let count = secure.receive(into: &buffer) {
    if count == 0 { break }
    // ... buffer[0..<count]
}
secure.close()
Beispiel: Klartext-TCP (Muster nach userland/Net.swift)
guard let address = NetConnection.resolve("example.com"),
      let connection = NetConnection(address: address, port: 80) else { return }
let request: StaticString = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"
_ = request.withUTF8Buffer { connection.send(Span(_unsafeElements: $0)) }
var buffer = InlineArray<1024, UInt8>(repeating: 0)
while let count = connection.receive(into: &buffer, timeout: 2_000_000_000), count > 0 {
    // ...
}
connection.close()
Beispiel: Claude fragen (apps/ask)
// manifest: capabilities = …, network.outgoing, device.camera
//           connects = api.anthropic.com      (+ uses-ai, uses-tls, uses-heap)
let result = Claude.ask(String(decoding: question, as: UTF8.self), jpeg: jpeg)
if let problem = result.problem {
    // problem: warum es keine Antwort gibt (kein Schlüssel, kein Netz …)
} else {
    answer = Array(result.text.utf8)
}

Ton & Mikrofon

Abspielen (sound.play): jedes Programm bekommt einen Ring, den der Mischer des Kerns spielt (48 kHz, Stereo, Int16). Hören (device.microphone): der Kern schreibt, was ein Mikrofon hört, in einen Ring pro Mikrofon (48 kHz, 16 Bit, mono/stereo, etwa 1,4 s), lesend in jedes hörende Programm eingeblendet; der Pegel jeder 10-ms-Periode steht daneben. Das Mikrofon läuft nur, solange jemand hört; die rote MIC-Anzeige nennt jedes Programm, und der Besitzer kann alle stoppen. Die Werkzeuge (Pegel, Auto-Pegel, Rauschtor, Stimmerkennung, Schnitt) kommen mit uses-heap.

SoundOutputAbspielen

AufrufWas er tutShape-Aufruf · Zeitbudget
Öffnen
static open() -> SoundOutput?Der Ring des Programms (bei jedem Aufruf derselbe); nil ohne sound.play oder Tongerät.≤ 1 mssound.open0x0030_0006
Beispiel
if output == nil { output = SoundOutput.open() }
aus apps/sound (gekürzt)
Abspielen
write(_ frames: UnsafePointer<Int16>, count: Int) -> IntSchreibt bis zu count Frames (links, rechts); gibt zurück, wie viele passten.
Beispiel
_ = chunk.withUnsafeBufferPointer { output.write($0.baseAddress!, count: count) }
aus apps/sound (gekürzt)
var room / queued / withDevice: IntPlatz jetzt / geschrieben, nicht gespielt / beim Gerät, noch nicht gehört.
Beispiel
while output.room >= 480 && position < total { /* nachfüllen */ }
aus apps/sound (gekürzt)
flush()Verwirft das Wartende (Stopp).
Beispiel
output?.flush()   // Stopp: Wartendes verwerfen
aus apps/sound (gekürzt)
Lautstärke
var volume: IntEigene Lautstärke 0 … 100 (über der Gesamtlautstärke).
static var status: (volume: Int, device: Bool, playing: Bool)Gesamtlautstärke, Gerät vorhanden, spielt gerade.≤ 5 µssound.status0x0030_0008
Beispiel
let hasDevice = SoundOutput.status.device
aus apps/settings (gekürzt)
static setMasterVolume(_ volume: Int) -> BoolGesamtlautstärke setzen (sound.volume).≤ 50 µssound.setVolume0x0030_0009
Beispiel
_ = SoundOutput.setMasterVolume(70)   // nur mit sound.volume
Muster (kurz, nicht aus einem Programm kopiert)
Schließen
close()Ring wird nicht mehr gemischt.≤ 5 µssound.close0x0030_0007
Konstanten
static let rate = 48_000 / let capacity: IntAbtastrate / Größe des Rings.

Microphone · MicrophoneRing · MicrophoneInfoHören

AufrufWas er tutShape-Aufruf · Zeitbudget
Mikrofon wählen & öffnen
Microphone.info(at index: Int) -> MicrophoneInfo?Das index-te Mikrofon (name, rate, channels, listening, id).≤ 20 µsmicrophone.list0x0031_0001
Beispiel
guard let info = Microphone.info(at: 0) else { problem = TextLine("There is no microphone."); return true }
aus apps/sound (gekürzt)
Microphone.open(_ id: Int? = nil) -> MicrophoneRing?Hören beginnen (nil: das erste Mikrofon); startet das Mikrofon.≤ 1 msmicrophone.open0x0031_0002
Beispiel
guard let opened = Microphone.open(info.id) else { return true }
ring = opened
ring?.skipToNow()
aus apps/sound (gekürzt)
Microphone.close(_ id: Int? = nil) -> BoolAufhören; das Mikrofon stoppt, wenn niemand mehr hört.≤ 1 msmicrophone.close0x0031_0003
Beispiel
Microphone.close()
aus apps/sound (gekürzt)
Proben lesen
ring.read(into: UnsafeMutablePointer<Int16>, frames: Int) -> (frames: Int, lost: Int)Wartende Frames kopieren; wer mehr als den Ring zurück ist, springt zur neuesten Sekunde (lost).
Beispiel
let got = raw.withUnsafeMutableBufferPointer { ring!.read(into: $0.baseAddress!, frames: 4800) }
aus apps/sound (gekürzt)
ring.skipToNow()Wartendes überspringen.
Zustand & Pegel
ring.state: State (running, idle, stoppedByOwner, failed)Was das Mikrofon tut – z. B. vom Besitzer gestoppt.
Beispiel
if let state = ring?.state, state == .stoppedByOwner { problem = TextLine("You stopped the microphone (MIC in the menu bar).") }
aus apps/sound (gekürzt)
ring.peakDecibels / rmsDecibels / periods / available / rate / channels / ringFramesPegel der letzten 10 ms (dBFS), Zähler, Wartendes.
Hörer (MIC-Anzeige)
Microphone.listeners(into names: inout InlineArray<512, UInt8>) -> (length: Int, count: Int)Wer gerade hört (MIC-Anzeige; system.protection).≤ 20 µsmicrophone.listeners0x0031_0004
Microphone.stopAll() -> BoolAlle Hörer stoppen (Stopp auf der MIC-Karte; system.protection).≤ 1 msmicrophone.stopAll0x0031_0005
Beispiel
_ = Microphone.stopAll()   // Stopp auf der MIC-Karte
aus apps/display (gekürzt)
Mikrofon anbieten
Microphone.provide(_ name: StaticString, channels: Int = 1) -> Int?Ein Mikrofon anbieten (microphone.provide).≤ 1 msmicrophone.provide0x0031_0006
Beispiel
guard let microphone = Microphone.provide("Test microphone", channels: 1) else { return }
aus apps/testmicrophone (gekürzt)
Microphone.push(_ id: Int, samples: Int) -> Bool / pushBuffer / pushRoomProben aus pushBuffer einspeisen (höchstens 80 ms Stereo).≤ 200 µsmicrophone.push0x0031_0007

Werkzeuge (uses-heap)Pegel, Dynamik, Stimme, Schnitt

AufrufWas er tutShape-Aufruf · Zeitbudget
Pegel
LevelMeter.measure(_ samples: Span<Int16>) -> SoundLevelRMS und Spitze in dBFS.
Beispiel
let level = LevelMeter.measure(span)   // level.peak, level.rms in dBFS
aus apps/sound (gekürzt)
LevelMeter.periods(_ samples: Span<Int16>, channels: Int = 1) -> [SoundLevel]Pegel je 10-ms-Periode.
LevelMeter.decibels / amplitude / linear / floor / fullScaleUmrechnungen dB ↔ Amplitude.
Dynamik
AutoLevel(target: Double = -12, minimumGain: -12, maximumGain: 24, silence: -55, ceiling: -1, channels: 1, gain: 0)Automatische Verstärkung mit Begrenzer; process(&samples).
Beispiel
autoLevel = AutoLevel(channels: channels, gain: Double(choices.gain))
autoLevel.process(&span)
aus apps/sound (gekürzt)
NoiseGate(threshold: Double = -50, holdMilliseconds: 200, fadeMilliseconds: 5, channels: 1)Rauschtor; process(&samples), isOpen.
Beispiel
gate = NoiseGate(channels: channels)
gate.process(&span)
aus apps/sound (gekürzt)
Stimme
VoiceDetector(margin: 10, minimumLevel: -65, crossings: 0.004...0.25, hangoverMilliseconds: 300, channels: 1, noiseFloor: nil)Spricht jemand? detect(period) -> Bool, level, noiseFloor.
Beispiel
voice = VoiceDetector(channels: channels)
speaking = voice.detect(span)
aus apps/sound (gekürzt)
VoiceDetector.voiceParts(_ samples: [Int16], channels: Int = 1, gapMilliseconds: Int = 300, shortestMilliseconds: Int = 200) -> [Range<Int>]Sprechstellen einer Aufnahme.
Beispiel
voiceParts = VoiceDetector.voiceParts(sound.samples, channels: sound.channels)
aus apps/sound (gekürzt)
Aufnahme & Schnitt
PreRecordBuffer(seconds: Int = 5, channels: Int = 1): append / drain / clear / framesDie letzten Sekunden vor dem Aufnahmestart.
Beispiel
preRecord = PreRecordBuffer(seconds: 5, channels: channels)
preRecord.append(span)            // jede Periode
let before = preRecord.drain()    // beim Aufnahmestart
aus apps/sound (gekürzt)
SoundEdit.trim / trimSilenceEnds / splitAtPauses / normalize / voiceBounds / overviewSchneiden, Stille kürzen, an Pausen teilen, normalisieren, Wellenbild.
Beispiel
let samples = SoundEdit.trimSilenceEnds(sound.samples, channels: sound.channels)
let parts = SoundEdit.splitAtPauses(sound.samples, channels: sound.channels)
aus apps/sound (gekürzt)
OverviewBuilder(framesPerColumn:channels:reserveSeconds:): append / overview(columns:)Wellenbild laufend aufbauen (WaveColumn min/max).
Format
SoundFormat.rate / periodFrames / frames(milliseconds:)48 kHz, 480 Frames pro 10 ms.
Beispiel: Mikrofon öffnen und lesen (apps/sound, Capture.swift)
// manifest: capabilities = …, sound.play, device.microphone   (+ uses-heap)
guard let info = Microphone.info(at: 0) else {
    problem = TextLine("There is no microphone.")
    return true
}
guard let opened = Microphone.open(info.id) else { return true }
ring = opened
ring?.skipToNow()
autoLevel = AutoLevel(channels: channels, gain: Double(choices.gain))
gate = NoiseGate(channels: channels)
voice = VoiceDetector(channels: channels)

// in der Schleife:
if ring!.state == .stoppedByOwner { problem = TextLine("You stopped the microphone (MIC in the menu bar).") }
while true {
    let got = raw.withUnsafeMutableBufferPointer { ring!.read(into: $0.baseAddress!, frames: 4800) }
    guard got.frames > 0 else { break }
    // ... in 10-ms-Perioden: autoLevel.process(&span), gate.process(&span),
    //     LevelMeter.measure(span), voice.detect(span)
}

// beim Ende:
Microphone.close()
Beispiel: Abspielen über den Mischer (apps/sound, Player.swift, gekürzt)
if output == nil { output = SoundOutput.open() }
guard let output else { return }
while output.room >= 480 && position < total {
    let count = min(output.room, 4800, total - position)
    // chunk mit count Stereo-Frames füllen (links, rechts)
    _ = chunk.withUnsafeBufferPointer { output.write($0.baseAddress!, count: count) }
    position += count
}
// Stopp: output.flush()

Kamera & Erkennung

Bilder kommen aus den Ringen des Kerns: mit device.camera wird der Bildring einer Kamera lesend ins Programm eingeblendet (neuestes Bild ohne Systemaufruf); Anbieter (camera.provide: Test-Kamera, USB-Treiber) schieben JPEG-Bilder hinein. Die Mac-Kamera nimmt nur auf, solange ein Programm den Ring offen hat.

Erkennung läuft im Programm Video (lokales Modell, keine Netzverbindung). Es veröffentlicht seine Treffer im Ring video.detections (publish = detections); Roboterprogramme abonnieren ihn mit subscribe = video.detections und lesen ohne Systemaufruf und ohne Warten – auch in einer Echtzeit-Schleife. Zu alte Bilder werden verworfen („stale pictures must not steer a robot“).

Camera · CameraInfo · CameraRing

AufrufWas er tutShape-Aufruf · Zeitbudget
Kamera wählen & öffnen
Camera.info(at index: Int) -> CameraInfo?Die index-te Kamera (name, width, height, frames, running, id).≤ 20 µscamera.list0x0020_0001
Beispiel
while let info = Camera.info(at: index) { list.append(info); index += 1 }
aus apps/video (gekürzt)
Camera.open(_ id: Int) -> CameraRing?Bildring einblenden; startet die Aufnahme.≤ 1 mscamera.ring0x0020_0006
Beispiel
if ring == nil, let info = Camera.info(at: 0) { ring = Camera.open(info.id) }
aus apps/server (gekürzt)
Camera.close(_ id: Int) -> BoolRing ausblenden; die Mac-Kamera stoppt, wenn ihn niemand mehr hat.≤ 1 mscamera.close0x0020_0009
Beispiel
Camera.close(cameras[chosenCamera].id)
aus apps/video (gekürzt)
Bilder lesen
ring.newest(after: UInt32, into buffer: UnsafeMutableRawBufferPointer) -> (frame: UInt32, count: Int, time: UInt64)?Kopiert das neueste Bild (JPEG), wenn es neuer als after ist: Nummer, Größe, Aufnahmezeit.
Beispiel
let taken = jpeg.withUnsafeMutableBytes { ring.newest(after: frame.number, into: $0) }
aus apps/video (gekürzt)
ring.latest / width / height / runningNeuestes vollständiges Bild, Maße, läuft die Kamera?
Kamera anbieten
Camera.provide(_ name: StaticString, width: Int, height: Int) -> Int?Anbieter einer Kamera werden (camera.provide).≤ 1 mscamera.provide0x0020_0007
Beispiel
guard let id = Camera.provide("Test camera", width: 640, height: 480) else { return }   // camera.provide
Muster (kurz, nicht aus einem Programm kopiert)
Camera.push(_ id: Int, count: Int) -> Bool / frameBuffer / frameRoomDas JPEG in frameBuffer (höchstens 448 KiB) als nächstes Bild einspeisen.≤ 500 µscamera.push0x0020_0008
Beispiel
// JPEG-Bytes nach Camera.frameBuffer kopieren (höchstens Camera.frameRoom), dann:
_ = Camera.push(id, count: jpeg.count)
Muster (kurz, nicht aus einem Programm kopiert)

PublicationsVeröffentlichen und abonnieren

AufrufWas er tutShape-Aufruf · Zeitbudget
Veröffentlichen
static detections() -> DetectionWriter<SharedRing>?Der eigene Erkennungsring zum Schreiben; nil ohne publish =.
Beispiel
nonisolated(unsafe) var publisher = Publications.detections()
aus apps/video (gekürzt)
Abonnieren
static subscription(_ index: Int = 0) -> DetectionReader<SharedRing>?Das index-te Abo (0 oder 1, Reihenfolge im Manifest).
Beispiel
guard let ring = Publications.subscription(0) else { System.log("rtvision: no subscription (subscribe line missing)"); return }
aus apps/rtvision (gekürzt)
Rohe Ringe
static ownRing() -> SharedRing? / subscriptionRing(_ index: Int) -> SharedRing?Dieselben Ringe als rohe Wörter (für andere Formate, z. B. den Arm-Ring).

DetectionReaderAbonnent

AufrufWas er tutShape-Aufruf · Zeitbudget
Lesen
newest(after: UInt32 = 0, now: UInt64, maximumAge: UInt64) -> DetectionBatch?Neuester Stapel, außer er ist älter als maximumAge ns.
Beispiel
if let batch = ring.newest(after: last, now: before, maximumAge: maximumAge) { last = batch.number }
aus apps/rtvision (gekürzt)
Klassen & Modell
classIndex(of name: StaticString) -> Int? / classByte(_:_:) / classCountKlasse über den Namen finden, Namen lesen.
Beispiel
if let cup = ring.classIndex(of: "cup"), let best = batch.best(of: cup) { aim(best.centreX, best.centreY) }
Muster (kurz, nicht aus einem Programm kopiert)
latest / modelGeneration / modelNameNeueste Stapelnummer; Generation steigt bei Modellwechsel (Klassen neu suchen).

DetectionWriterHerausgeber (Video)

AufrufWas er tutShape-Aufruf · Zeitbudget
Klassen & Modell
setClassName(_ index: Int, length: Int, _ byteAt: (Int) -> UInt8) / classesDone(count: Int)Klassennamen einmal schreiben.
Beispiel
publisher?.setClassName(classIndex, length: name.count) { name[$0] }
publisher?.classesDone(count: model.classNames.count)
aus apps/video (gekürzt)
setModel(length: Int, _ byteAt: (Int) -> UInt8)Name des aktiven Modells (Generation steigt).
Schreiben
mutating publish(_ batch: DetectionBatch) -> UInt32Stapel als nächsten schreiben; seine Nummer.
Beispiel
publisher!.publish(batch)
aus apps/video (gekürzt)
var readers: IntWie viele Programme gerade lesen.

DetectionBatch · Sighting

AufrufWas er tutShape-Aufruf · Zeitbudget
Stapel
DetectionBatch: number, frame, captured, done, width, height, count, objects (bis 32)Was ein Bild zeigte.
best(of classIndex: Int) -> Sighting?Bester Treffer einer Klasse.
Objekt
Sighting(classIndex: Int, score: Double, x0: Double, y0: Double, x1: Double, y1: Double)Ein Objekt: Klasse, Sicherheit, Kasten als Bruchteile des Bildes.
Beispiel
batch.objects[batch.count] = Sighting(classIndex: detection.classIndex, score: Double(detection.score),
                                      x0: x0, y0: y0, x1: x1, y1: y1)
batch.count += 1
aus apps/video (gekürzt)
Sighting.centreX / centreYMitte des Kastens.
Ring-Format
DetectionRing.format(_:) / magic / version / OffsetsAufbau des Rings (eine 16-KiB-Seite).

Shape-Modul vision / cameraFür Reflex und den Kern

AufrufWas er tutShape-Aufruf · Zeitbudget
Für Reflex und den Kern
vision.detectionsNeueste Erkennungen (Ring von Video).≤ 10 µsvision.detections0x0021_0001
vision.mask / vision.motion / vision.markersFarbmaske mit Flecken, Bewegung zwischen zwei Bildern, AprilTag-Marker.≤ 2 msvision.mask0x0021_0002
camera.frame / camera.toWorld / camera.fromWorldNeuestes Bild; Bild- ↔ Weltkoordinaten (Kalibrierung nötig).≤ 2 µscamera.toWorld0x0020_0003
Beispiel: Neuestes Kamerabild holen und dekodieren (apps/video, Pipeline.swift)
// manifest: capabilities = …, device.camera   publish = detections
var list: [CameraInfo] = []
var index = 0
while let info = Camera.info(at: index) { list.append(info); index += 1 }
ring = Camera.open(list[0].id)

func takeFrame() -> Bool {
    guard let ring else { return false }
    let taken = jpeg.withUnsafeMutableBytes { ring.newest(after: frame.number, into: $0) }
    guard let taken else { return false }
    guard case .success(let picture) = Picture.decode(Array(jpeg[0..<taken.count])) else { return false }
    // ... erkennen, zeichnen
    return true
}
Beispiel: Erkennungen veröffentlichen (apps/video)
nonisolated(unsafe) var publisher = Publications.detections()

var batch = DetectionBatch()
batch.frame = shown.number
batch.captured = shown.captured
batch.done = done
batch.width = width; batch.height = height
batch.objects[batch.count] = Sighting(classIndex: 3, score: 0.82, x0: 0.1, y0: 0.2, x1: 0.4, y1: 0.6)
batch.count += 1
publisher!.publish(batch)
Beispiel: Erkennungen in einer 1-kHz-Schleife lesen (apps/rtvision)
// manifest: realtime = 1000 200 log
//           subscribe = video.detections
guard let ring = Publications.subscription(0) else {
    System.log("rtvision: no subscription (subscribe line missing)")
    return
}
var last: UInt32 = 0
while true {
    let now = System.time
    if let batch = ring.newest(after: last, now: now, maximumAge: 5_000_000_000) {
        last = batch.number
        // batch.objects[0..<batch.count], ring.classByte(classIndex, at) für den Namen
    }
    System.waitForNextPeriod()
}

Bilder, PDF & QR

Mit uses-image (braucht uses-heap): Bilder dekodieren und kodieren, PDF schreiben und lesen. Ein Picture hält RGBA-Pixel. PDF lesen ist ungeprüfte Eingabe – die Programme Reader und Image lesen deshalb nur das eine Dokument, das ihnen übergeben wurde. QR-Codes mit uses-qr (ohne Heap).

Picture

AufrufWas er tutShape-Aufruf · Zeitbudget
Bild
Picture(width: Int = 0, height: Int = 0, pixels: [UInt8] = [], scale: Int = 1)Ein Bild aus RGBA-Pixeln.
Beispiel
var picture = Picture(width: 64, height: 48, pixels: [UInt8](repeating: 255, count: 64 * 48 * 4))
aus apps/apicheck (gekürzt)
static let maxFile / maxPixelsGrenzen gegen zu große Eingaben.
PictureError.textKurzer Satz für Meldungen.
Dekodieren
static decode(_ file: [UInt8]) -> Result<Picture, PictureError>PNG, JPEG, GIF, WebP, BMP oder TIFF nach Signatur.
Beispiel
guard case .success(let picture) = Picture.decode(Array(jpeg[0..<taken.count])) else { return false }
aus apps/video (gekürzt)

Formate

AufrufWas er tutShape-Aufruf · Zeitbudget
PNG & JPEG
PNG.encode(_ picture: Picture) -> [UInt8]? / decode / hasSignature / size(_:)PNG verlustfrei (RGB wenn deckend, sonst RGBA); Größe ohne Dekodieren.
Beispiel
guard let png = PNG.encode(picture), case .success(let decodedPNG) = PNG.decode(png) else { return }
aus apps/apicheck (gekürzt)
JPEG.encode(_ picture: Picture, quality: Int = 85) -> [UInt8]? / decode / hasSignatureJPEG (Baseline, 4:2:0, Qualität 1 … 100).
Beispiel
guard let jpeg = JPEG.encode(picture, quality: 85) else { return }
aus apps/apicheck (gekürzt)
GIF & WebP
GIF.decode / hasSignature / maxFramesGIF, erstes Bild.
GIFAnimation.parse(_ file: [UInt8]) -> Result<GIFAnimation, PictureError>Animiertes GIF: advance(), show(_:), delay(_:), frameCount, pixels.
Beispiel
guard case .success(var animation) = GIFAnimation.parse(bytes) else { return }
let wait = animation.advance()   // ms bis zum nächsten Bild; animation.pixels zeigen
Muster (kurz, nicht aus einem Programm kopiert)
WebP.decode / hasSignatureWebP (verlustbehaftet und verlustfrei).
WebPAnimation.parse(_ file: [UInt8]) -> Result<WebPAnimation, PictureError>Animiertes WebP, wie GIFAnimation.
BMP & TIFF
BMP.encode / decode / hasSignature, TIFF.encode / decode / hasSignatureBMP und TIFF lesen und schreiben.
Entpacken & Senken
Inflater.zlib(_:limit:) / zlib(_:size:) / gzip(_:limit:)zlib/gzip entpacken (mit Größengrenze).
PNGSink / ByteCollector (ByteSink)Ziele für Bytes beim Kodieren.

PDFDocumentPDF schreiben

AufrufWas er tutShape-Aufruf · Zeitbudget
Dokument
PDFDocument(width: Double = PDFDocument.a4.width, height: Double = PDFDocument.a4.height)Neues Dokument (Maße in Punkt).
Beispiel
var pdf = PDFDocument()   // A4
aus apps/image (gekürzt)
newPage() / data() -> [UInt8]Neue Seite / das fertige PDF.
Beispiel
pdf.newPage()
let bytes = pdf.data()   // dann File.open(…, mode: .replace) und write
Muster (kurz, nicht aus einem Programm kopiert)
data(userPassword: [UInt8], ownerPassword: [UInt8], random: [UInt8]) -> [UInt8]Verschlüsseltes PDF.
Inhalt setzen
text(_ text: String, x: Double, y: Double, size: Double = 11, font: Font = .regular, colour: (UInt8, UInt8, UInt8) = (0, 0, 0))Text setzen.
Beispiel
pdf.text(name.isEmpty ? "Picture" : name, x: margin, y: margin + 14, size: 16, font: .bold)
aus apps/image (gekürzt)
picture(_ picture: Picture, x:y:width:height:, jpeg: [UInt8]? = nil) -> BoolBild einsetzen (optional das JPEG direkt).
line(x0:y0:x1:y1:width:colour:) / rectangle(x:y:width:height:fill:)Linie, gefülltes Rechteck.
Maße
static width(_ text: String, size: Double, font: Font = .regular) -> Double / a4Textbreite, A4-Maße.

PDFReaderPDF lesen

AufrufWas er tutShape-Aufruf · Zeitbudget
Öffnen
init(_ bytes: [UInt8], password: [UInt8] = []) throws(PDFReadError)Öffnet ein PDF (auch verschlüsselt).
Beispiel
var opened = try PDFReader(fileBytes, password: password)
aus apps/reader (gekürzt)
Seiten & Text
pageCount / page(_ index: Int) -> PDFPage?Seiten (width, height, rotate, content, resources).
Beispiel
guard var reader = try? PDFReader(bytes), let page = reader.page(0) else { return }
aus apps/files (gekürzt)
text(page index: Int) -> [PDFTextRun]Textstücke einer Seite mit Lage und Größe.
Navigation
outline() / links(page:)Inhaltsverzeichnis, Links.
Beispiel
outlineItems = opened.outline()   // [(level, title, page)]
aus apps/reader (gekürzt)
Rohdaten & Rechte
object / resolve / streamBytes / decode / decodeBytes / trailerRohe Objekte für eigene Auswertung (PDFObject, PDFDictionary).
isEncrypted / openedAsOwner / permissions / textString(_:) / maximumFile / maximumStreamVerschlüsselung, Rechte, Grenzen.

QRCode (uses-qr)

AufrufWas er tutShape-Aufruf · Zeitbudget
Kodieren
static encode(_ bytes: Span<UInt8>, correction: QRCorrection = .medium, mask: Int? = nil) -> QRCode?QR-Code im Byte-Modus (Versionen 1 … 10, Stufen L/M/Q/H).
Beispiel
invitationQR = QRCode.encode(bytes.span.extracting(0..<text.count), correction: .medium)
aus apps/server (gekürzt)
static capacity(version: Int, correction: QRCorrection) -> IntWie viele Bytes passen.
Lesen
isDark(x: Int, y: Int) -> Bool / size / version / correction / maskModule lesen.
Beispiel: Fenster als PNG oder JPEG sichern (sdk-apps/about)
guard let bytes = jpeg ? JPEG.encode(picture, quality: 90) : PNG.encode(picture),
      case .success(let file) = jpeg ? File.open("window.jpg", mode: .replace) : File.open("window.png", mode: .replace) else {
    saved = TextLine("Could not save the picture")
    return
}
bytes.withUnsafeBufferPointer { buffer in _ = file.write(Span(_unsafeElements: buffer)) }
_ = file.close()
Beispiel: Ein Bild als PDF ausgeben (apps/image)
var pdf = PDFDocument()
let margin = 40.0
pdf.text(name.isEmpty ? "Picture" : name, x: margin, y: margin + 14, size: 16, font: .bold)
pdf.text(String(shown.width) + " x " + String(shown.height) + " pixels · SOFTrobots OS Image",
         x: margin, y: margin + 32, size: 10, colour: (110, 110, 110))
_ = pdf.picture(shown, x: margin, y: margin + 50, width: 400, height: 300)
let bytes = pdf.data()
Beispiel: PDF öffnen und Seiten lesen (apps/reader)
do {
    var opened = try PDFReader(fileBytes, password: password)
    for index in 0..<opened.pageCount {
        if let page = opened.page(index) {
            let turned = page.rotate == 90 || page.rotate == 270
            pageSizes.append(turned ? (page.height, page.width) : (page.width, page.height))
        }
    }
    let runs = opened.text(page: 0)     // [PDFTextRun]: text, x, y, size, width
} catch {
    // falsches Passwort, beschädigt …
}
Beispiel: QR-Code für eine Einladung (apps/server)
invitationQR = QRCode.encode(bytes.span.extracting(0..<text.count), correction: .medium)
guard let code = invitationQR else { return }
let modules = code.size + 8
for y in 0..<code.size { for x in 0..<code.size where code.isDark(x: x, y: y) { /* Modul füllen */ } }

Video- & Tonformate

Container und Codecs für Aufnahme und Wiedergabe: Matroska/WebM (uses-image), VP9 (uses-video), Opus (uses-opus), WAV und FLAC (mit uses-heap). Video nimmt MKV mit MJPEG, Ton und Erkennungen auf und spielt WebM (VP9 + Opus).

MatroskaWriterMKV schreiben (MJPEG, Ton, Erkennungen)

AufrufWas er tutShape-Aufruf · Zeitbudget
Anlegen
init(width: Int, height: Int, detections: Bool = false, soundRate: Int = 0, soundChannels: Int = 1)Neuer Schreiber.
mutating header(app: StaticString = "") -> [UInt8]Kopf der Datei.
Spuren schreiben
mutating frame(_ jpeg: [UInt8], milliseconds: Int) -> [UInt8]Ein Bild.
mutating sound(_ samples: [UInt8], milliseconds: Int) -> [UInt8]Ein Stück Ton.
mutating detections(_ text: [UInt8], milliseconds: Int) -> [UInt8]Erkennungen als Untertitelspur.
Abschließen
mutating finish() -> [UInt8] / frames / lastTimeAbschluss (Index).

MatroskaIndex · MatroskaTrack · MatroskaFrameMKV/WebM lesen

AufrufWas er tutShape-Aufruf · Zeitbudget
Lesen & springen
MatroskaIndex(size: Int, read: @escaping (Int, Int) -> [UInt8]) throws(MatroskaError)Liest die Struktur über eine Lesefunktion (ohne alles zu laden).
Beispiel
guard let index = try? MatroskaIndex(size: Int(size), read: reader) else { return }
aus apps/video (gekürzt)
tracks / frames / duration / docType / seek(_ milliseconds: Int, track: Int) -> IntSpuren, Rahmen (track, time, offset, length, key), Springen.
Spuren
MatroskaTrack.isCodec(_ name: StaticString) -> Bool / codec / codecPrivate / sampleRate / widthCodec einer Spur ("V_VP9", "A_OPUS").
Beispiel
opus = track.isCodec("A_OPUS") ? OpusDecoder(head: track.codecPrivate) : nil
aus apps/video (gekürzt)
MatroskaID.ebml / cluster / tracks / cues …Element-IDs.

VP9Decoder · OpusDecoder

AufrufWas er tutShape-Aufruf · Zeitbudget
Video (VP9)
VP9Decoder() / mutating decode(_ packet: [UInt8]) throws(VP9Error) -> VP9Picture?Ein VP9-Paket dekodieren.
Beispiel
guard let shown = try? vp9.decode(bytes) else { return }
let picture = Picture(width: shown.width, height: shown.height, pixels: shown.rgba())
aus apps/video (gekürzt)
VP9Picture.rgba() -> [UInt8] / y, u, v, yStride, uvStride, width, heightBild als RGBA oder YUV-Ebenen.
Ton (Opus)
OpusDecoder(head: [UInt8]) / mutating decode(_ packet: [UInt8]) throws(OpusError) -> [Float] / reset()Opus-Pakete zu Float-Proben; channels, preSkip, gain.

WAV · FLAC

AufrufWas er tutShape-Aufruf · Zeitbudget
WAV
WAV(_ file: Span<UInt8>)Liest einen WAV-Kopf: rate, channels, bits, float, frames, dataStart, milliseconds.
Beispiel
} else if let wav = head.withUnsafeBufferPointer({ WAV(Span(_unsafeElements: $0)) }) {
aus apps/sound (gekürzt)
WAV.render(_ file: Span<UInt8>, from first: Int, count: Int, into out: UnsafeMutablePointer<Int16>) -> Int / sample(_:_:_:)Frames als Int16 (Stereo 48 kHz für SoundOutput).
FLAC schreiben
FLACEncoder(rate: Int, channels: Int, bits: Int, blockSize: Int = 4096, maxLPCOrder: Int = 12)FLAC-Kodierer.
Beispiel
let encoder = FLACEncoder(rate: rate, channels: channels, bits: 16)
file.pending = encoder.header()
aus apps/sound (gekürzt)
FLACEncoder.header() / encode(_ samples: Span<Int16> | Span<Int32>) -> [UInt8] / finish()Kopf, Blöcke, Abschluss (mit endgültigem Kopf).
FLAC lesen
FLACDecoder(_ data: [UInt8]) / init(length:read:) throws(FLACError)FLAC lesen (ganz oder über eine Lesefunktion).
Beispiel
if var decoder = try? FLACDecoder(flac) {
    while let block = try? decoder.nextBlock(), !block.isEmpty { for value in block { decoded.append(Int16(truncatingIfNeeded: value)) } }
}
aus apps/miccheck (gekürzt)
FLACDecoder.nextBlock() throws(FLACError) -> [Int32] / seek(toSample:) / info: FLACInfoNächster Block, Springen, Stream-Infos (rate, channels, bits, totalSamples, md5).
Beispiel: Aufnahme als MKV beginnen und Bilder anhängen (apps/video, Recording.swift)
guard case .success(let file) = File.open(path, mode: .replace) else { return false }
var writer = MatroskaWriter(width: picture.width * picture.scale, height: picture.height * picture.scale, detections: true,
                            soundRate: recordSoundFormat.rate, soundChannels: recordSoundFormat.channels)
let header = writer.header()
_ = header.withUnsafeBufferPointer { file.write(Span(_unsafeElements: $0)) }

// je Kamerabild:
let milliseconds = Int((System.time &- recordStarted) / 1_000_000)
let piece = writer.frame(Array(jpeg[0..<jpegCount]), milliseconds: milliseconds)
_ = piece.withUnsafeBufferPointer { file.write(Span(_unsafeElements: $0)) }
Beispiel: FLAC-Datei schreiben (apps/sound, SoundFile.swift)
var encoder = FLACEncoder(rate: rate, channels: channels, bits: 16)
pending = encoder.header()
// je 10-ms-Periode:
pending += encoder.encode(samples)          // Span<Int16>
// am Ende:
let (tail, finalHeader) = encoder.finish()

Archive (ZIP)

Mit uses-archive (braucht uses-heap): ZIP lesen und schreiben; ein Passwort bedeutet immer WinZip AES-256. Dazu rohes DEFLATE und CRC32.

ZipArchive · ZipEntryLesen

AufrufWas er tutShape-Aufruf · Zeitbudget
Inhalt lesen
ZipArchive.entries<S: ByteSource>(_ source: inout S) -> Result<[ZipEntry], ZipError>Inhaltsverzeichnis lesen.
Beispiel
var source = bytesSource            // ein ByteSource über die Datei
guard case .success(let entries) = ZipArchive.entries(&source) else { return }
Muster (kurz, nicht aus einem Programm kopiert)
ZipEntry: name, size, compressedSize, crc, isDirectory, isEncrypted, isLink, method, aes…Ein Eintrag.
Entpacken
ZipArchive.extract(_ entries: [ZipEntry], from source: S, to destination: inout D, password: [UInt8]?) -> Result<ZipSummary, ZipError>Einträge entpacken (ZipSummary: files, folders, bytes, skipped).
ZipArchive.safePath(_ entry: ZipEntry) -> [UInt8]?Sicherer Pfad (kein „..“, kein absoluter Pfad).

ZipWriterSchreiben

AufrufWas er tutShape-Aufruf · Zeitbudget
Schreiben
beginFile(_ file: Span<UInt8>, time: ZipTime, password: [UInt8]?, salt: Span<UInt8>, _ sink: inout Sink) -> Result<Void, ZipError>Datei beginnen (mit Passwort: AES-256).
write(_ data: Span<UInt8>, _ sink: inout Sink) / endFile(_:) / addFolder(_:time:_:) / finish(_:)Daten, Dateiende, Ordner, Abschluss.
Zeit
ZipTime(unixSeconds: Int64, offsetSeconds: Int64)Zeitstempel.

Deflate & Prüfsummen

AufrufWas er tutShape-Aufruf · Zeitbudget
Deflate & Prüfsummen
Deflater: compress(_:_:) / finish(_:) / reset()Rohes DEFLATE packen.
StreamInflater: next(_ source: inout S) -> Result<Range<Int>?, InflateError> / reset() / totalRohes DEFLATE stückweise entpacken.
CRC32: add(_ bytes: Span<UInt8>) / checksum / reset()CRC-32.
Beispiel
var crc = CRC32()
crc.add(data.span)
let sum = crc.checksum
Muster (kurz, nicht aus einem Programm kopiert)

Roboter & Sicherheit

Bewegungen sind Effekte: Programme bewegen nie selbst Motoren, sondern bitten den Aktor-Dienst (actuators, Recht safety.actuator) über den Kanal actuator (Recht ipc). Der Dienst prüft jede Bewegung gegen die Sicherheitsgrenzen (begrenzt oder lehnt ab) und spricht über CAN mit den Motoren. Der Not-Halt hält der Kern selbst: jedes Programm darf ihn lesen, auslösen und lösen nur mit safety.stop. Programme sehen eine Generation, die sich mit jedem Halt und jeder Freigabe ändert (ungerade = gehalten).

Für Arme gibt es Modelle (SR-6, SO-101, UR5e, UR3e, FR3, xArm6, myCobot 280) mit Vorwärts- und Rückwärtskinematik, eine simulierte Greiferkamera und simulierte Motoren; der Aktor-Dienst veröffentlicht den Arm-Zustand (publish = arm).

System (Not-Halt)

AufrufWas er tutShape-Aufruf · Zeitbudget
Zustand lesen
System.emergencyStopped: BoolGilt der Not-Halt gerade?≤ 2 µssafety.state0x0100_0001
Beispiel
let stopped = System.emergencyStopped
aus apps/arm (gekürzt)
System.safetyGeneration: UInt64Ändert sich mit jedem Halt und jeder Freigabe; ungerade = gehalten.≤ 2 µssafety.state0x0100_0001
Beispiel
let now = System.safetyGeneration
if now != safety { safety = now; motors.emergencyStop(now & 1 == 1) }
aus apps/actuators (gekürzt)
System.safetyState: SafetyStategeneration, stopped, sinceStop.≤ 2 µssafety.state0x0100_0001
System.microsecondsSinceStop: UInt64µs seit dem letzten Halt.≤ 2 µssafety.state0x0100_0001
Auslösen & lösen
System.emergencyStop(reason: UInt64) -> BoolAlle Aktoren sofort anhalten (safety.stop).≤ 20 µssafety.stop0x0100_0002
Beispiel
let stopped = System.emergencyStop(reason: 5)
aus apps/server (gekürzt)
System.releaseEmergencyStop() -> BoolHalt lösen; Aktoren warten auf den nächsten Befehl ihrer Programme.≤ 20 µssafety.release0x0100_0003
Beispiel
System.releaseEmergencyStop()
aus apps/arm (gekürzt)

ArmModelEin Arm: Gelenke, Reichweite, Werkzeug

AufrufWas er tutShape-Aufruf · Zeitbudget
Arm wählen
static let sr6 / static model(_ index: Int) -> ArmModelDer simulierte Tischarm / Modell nach Index.
static let ids: InlineArray<7, StaticString>"sr6", "so101", "ur5e", "ur3e", "fr3", "xarm6", "mycobot280".
static chosen<let n: Int>(_ id: InlineArray<n, UInt8>, count: Int) -> ArmModel / index(of:count:)Arm nach den Bytes von arm = (Settings › Robot); SR-6 wenn leer.
Beispiel
let model = ArmModel.chosen(state.id, count: state.idLength)
aus apps/server (gekürzt)
Kinematik
forward(_ q: ArmAngles) -> ArmPoseGelenkwinkel → Punkte des Arms und Werkzeugpose.≤ 2 µsarm.forward0x0102_0001
Beispiel
let pose = model.forward(q)   // pose.tip: ArmPoint in mm
Muster (kurz, nicht aus einem Programm kopiert)
inverse(x: Double, y: Double, z: Double, pitch: Double, near: ArmAngles) -> ArmAngles?Werkzeugspitze nach (x, y, z) mit Neigung, am nächsten zu near; nil außer Reichweite.≤ 20 µsarm.inverse0x0102_0002
Beispiel
if let q = model.inverse(x: 300, y: 0, z: 120, pitch: -Maths.pi / 2, near: current) { goTo(q) }
Muster (kurz, nicht aus einem Programm kopiert)
Gelenke & Daten
joint(_ index: Int) -> ArmJoint / count / jointsGelenke (name, low, high, fastest, strongest, rest).≤ 10 µsarm.joints0x0102_0003
name, maker, id, reach, payload, tool, restAngles, closedForm, urdf …Beschreibung des Arms.

ArmAngles · ArmPoint · ArmPose · ArmCamera

AufrufWas er tutShape-Aufruf · Zeitbudget
ArmAngles · ArmPoint · ArmPose · ArmCamera
ArmAngles() / subscript(_ index: Int) -> DoubleGelenkwinkel in Radiant (bis 7).
Beispiel
var angles = ArmAngles()
angles[joint] = Double(state.positions[joint]) / 100 * Double.pi / 180
aus apps/server (gekürzt)
ArmPoint(_ x: Double = 0, _ y: Double = 0, _ z: Double = 0)Punkt in Welt-mm (x vor, y links, z hoch).
ArmPose: points, pointCount, tip, approach, pitchWo der Arm ist.
ArmCamera(_ model: ArmModel, _ q: ArmAngles): toWorld(x:y:depth:) / fromWorld(_:)Greiferkamera 640 × 480: Pixel ↔ Weltpunkt.≤ 2 µscamera.toWorld0x0020_0003

SimulatedMotorsDie simulierten Motoren (Aktor-Dienst)

AufrufWas er tutShape-Aufruf · Zeitbudget
Anlegen
SimulatedMotors(_ model: ArmModel = .sr6)Arm in Ruhestellung.
Beispiel
motors = SimulatedMotors(model)
aus apps/actuators (gekürzt)
Steuern
set(joint: Int, target value: Int32, speed limit: Int32)Sollwert in 0,01° und Geschwindigkeit.
Beispiel
motors.set(joint: jointNumber - 1, target: grantedTarget, speed: grantedSpeed)
aus apps/actuators (gekürzt)
step(milliseconds: Int) / emergencyStop(_ on: Bool)Bewegung simulieren / sofort bremsen.
Zustand
moving / position / velocity / target / speed / stoppedZustand.

ArmRing · ArmStateArm-Zustand ohne Systemaufruf lesen

AufrufWas er tutShape-Aufruf · Zeitbudget
Lesen
ArmRing.read<Ring: SharedWords>(_ ring: Ring) -> ArmState?Neuester vollständiger Zustand (Abo subscribe = actuators.arm).
Beispiel
guard let ring = Publications.subscriptionRing(0), let state = ArmRing.read(ring) else { return page }
aus apps/server (gekürzt)
ArmState: positions, moving, stopped, chosen, id, updatesGelenkstellungen und Flags.
Schreiben (Dienst)
ArmRing.write(_:_:)Schreibt der Aktor-Dienst.

CANFrameCAN- und CAN-FD-Rahmen

AufrufWas er tutShape-Aufruf · Zeitbudget
Rahmen bauen
CANFrame(identifier: UInt32, fd: Bool = true)Rahmen bis 64 Datenbytes.
Beispiel
var setpoint = CANFrame(identifier: UInt32(0x100 + jointNumber))
setpoint.setInt32(grantedTarget, at: 0)
setpoint.setInt32(grantedSpeed, at: 4)
aus apps/actuators (gekürzt)
int32(at offset: Int) -> Int32 / setInt32(_:at:)Werte im Rahmen (little-endian).
Senden & kodieren
encode(into:) -> Int / static decode(_:count:) -> CANFrame?Als Kanalnachricht.
channel.send(_ frame: CANFrame) -> BoolRahmen über einen Kanal senden.
Beispiel
_ = bus!.send(stop)   // stop: CANFrame(identifier: 0x000)
aus apps/actuators (gekürzt)

Shape-Module robot / arm / safetyFür Reflex und den Kern

AufrufWas er tutShape-Aufruf · Zeitbudget
Bewegen
robot.moveBewegung starten (Effekt; der Aktor-Dienst führt sie aus).≤ 20 µsrobot.move0x0101_0001
robot.stateWinkel, Geschwindigkeit, Strom, Temperatur eines Gelenks.≤ 5 µsrobot.state0x0101_0002
arm.torqueDrehmoment für Echtzeitregelung (nur Echtzeit-Handler).≤ 1 µsarm.torque0x0102_0004
Not-Halt
safety.state / stop / releaseNot-Halt lesen, auslösen, lösen.≤ 20 µssafety.stop0x0100_0002
Beispiel: Gelenk 1 über den Aktor-Dienst bewegen, Not-Halt (apps/arm)
// manifest: capabilities = system.log, ipc, ui.surface, safety.stop
guard var actuator = Channel("actuator") else { System.log("arm: no actuator channel"); return }

private func move(_ to: Int32, _ actuator: inout Channel) {
    var bytes = InlineArray<9, UInt8>(repeating: 0)
    bytes[0] = 1                                   // 1 = move
    let values: InlineArray<2, Int32> = [to, 3_000] // Ziel und Geschwindigkeit in 0,01°
    for index in 0..<2 {
        let bits = UInt32(bitPattern: values[index])
        for byte in 0..<4 { bytes[1 + 4 * index + byte] = UInt8(truncatingIfNeeded: bits >> UInt32(8 * byte)) }
    }
    _ = actuator.send(bytes.span)
    actuator.notify()
}

// Antworten: 2 = Bewegung angenommen/begrenzt/abgelehnt, 3 = Status eines Gelenks
while let count = actuator.receive(into: &message), count >= 13 { /* ... */ }

// Not-Halt-Knopf:
System.emergencyStop(reason: 1)
// Freigeben:
System.releaseEmergencyStop()
Beispiel: Not-Halt im Aktor-Dienst beobachten (apps/actuators)
var safety = System.safetyGeneration
if safety & 1 == 1 { motors.emergencyStop(true) }
while true {
    let now = System.safetyGeneration
    if now != safety { motors.emergencyStop(now & 1 == 1); safety = now }
    // ...
}
Beispiel: Arm-Zustand lesen und Kinematik (apps/server, Live.swift)
// manifest: subscribe = actuators.arm
guard let ring = Publications.subscriptionRing(0), let state = ArmRing.read(ring) else { return }
let model = ArmModel.chosen(state.id, count: state.idLength)
var q = ArmAngles()
for joint in 0..<model.count {
    q[joint] = Double(state.positions[joint]) / 100 * Double.pi / 180   // 0,01° → Radiant
}
let pose = model.forward(q)                       // pose.tip, pose.points
if let reach = model.inverse(x: 300, y: 0, z: 120, pitch: -Maths.pi / 2, near: q) { _ = reach }

Echtzeit

Ein Echtzeit-Programm schreibt realtime = <Periode µs> <Budget µs> [log|stop|restart|stop-actuators] ins Manifest. Der Kern lässt es nur zu, wenn das Budget auf dem Echtzeit-Kern Platz hat, und startet jede Periode pünktlich. Echtzeit-Programme haben keinen Heap, keine Dateien und keine Settings (keine uses-heap-Datei); sie rufen System.waitForNextPeriod() einmal pro Periode. Daten tauschen sie ohne Systemaufruf: Abos (subscribe =), Arm-Ring, Kanäle mit receivePolling und Messreihen (Recorder, record = …).

System (Echtzeit)

AufrufWas er tutShape-Aufruf · Zeitbudget
Periode
System.waitForNextPeriod()Die Arbeit dieser Periode ist getan; kehrt zum Beginn der nächsten zurück.periodWait (Systemaufruf 4)
Beispiel
System.waitForNextPeriod()   // am Ende jeder Periode
aus apps/control (gekürzt)
System.time: UInt64Nanosekunden seit dem Start.≤ 1 µssystem.now0x0002_0002
Statistik
System.realtimeStats(restartWorst: Bool = false) -> RealtimeStats?Perioden, verpasste Fristen, schlechteste und mittlere Startlatenz (Sekunden), Periode; nil für normale Programme.≤ 2 µssystem.realtime0x0002_000D
Beispiel
if System.time >= nextReport, let stats = System.realtimeStats(restartWorst: true) { /* stats.missed, stats.worstLatency */ }
aus apps/control (gekürzt)

RecorderMessreihen ohne Systemaufruf

AufrufWas er tutShape-Aufruf · Zeitbudget
Aufzeichnen
static attach() -> Recorder?Die Aufnahme des Programms; nil ohne record =-Zeile.
Beispiel
guard let recorder = Recorder.attach() else { return }   // braucht record = … im Manifest
Muster (kurz, nicht aus einem Programm kopiert)
push(time: UInt64, _ data: Span<UInt8>) -> BoolEinen Satz anhängen (Zeit + bis recordSize − 8 Bytes); false wenn der Ring voll war (gezählt).
Beispiel
recorder.push(time: System.time, sample.span)
aus apps/rtrecord (gekürzt)
Zustand
dropped / recordSize / flightVerworfene Sätze, Satzgröße, Flugschreiber (überschreibt das Älteste, gesichert nur bei Not-Halt oder Absturz).
Beispiel: 1-kHz-Regelschleife mit Echtzeit-Vertrag (apps/control)
// manifest: realtime = 1000 100      (Periode 1000 µs, Budget 100 µs; keine uses-heap-Datei)
@c(app_main)
func appMain() {
    var position: Int64 = 0, target: Int64 = 10_000, integral: Int64 = 0, previousError: Int64 = 0
    var nextReport = System.time + 5_000_000_000
    while true {
        let error = target - position
        integral += error
        let derivative = error - previousError
        previousError = error
        position += (error * 8 + integral / 64 + derivative * 2) / 256
        if System.time >= nextReport, let stats = System.realtimeStats(restartWorst: true) {
            nextReport += 5_000_000_000
            var line = TextLine("control: ")
            line.append(decimal: stats.missed)
            line.append(" missed, worst ")
            line.append(decimal: UInt64(stats.worstLatency * 1e9))
            line.append(" ns")
            System.log(line)
        }
        System.waitForNextPeriod()
    }
}
Beispiel: Messwerte aufzeichnen
// manifest: record = samples 64 5 3600     (Name, Satzgröße, Sekunden im Ring, Sekunden auf Platte)
guard let recorder = Recorder.attach() else { return }
var record = InlineArray<56, UInt8>(repeating: 0)
// ... record füllen
_ = recorder.push(time: System.time, record.span)

Programme & System

Information über das System, Zeit, Protokoll und Zufall stehen jedem Programm offen (das Protokoll braucht system.log). Andere Programme starten, stoppen und auflisten braucht apps.launch; was jedes Programm verbraucht, system.status. Updates, Strom und Geräteschutz sind Rechte der Systemprogramme.

System (allgemein)

AufrufWas er tutShape-Aufruf · Zeitbudget
Information
System.info() -> SystemStatus?apiVersion, version, deviceName, uptime, wallClock, cpus, realtimeCPU, memoryTotal/Free, screenWidth/Height, emergencyStopped, dataAvailable, dataProtected, memoryLow, deviceKey, arm, address …≤ 5 µssystem.info0x0002_0001
Beispiel
if let info = System.info() { info.arm.forEachByte { if count < 16 { id[count] = $0; count += 1 } } }
aus apps/actuators (gekürzt)
System.processor(_ index: Int) -> ProcessorInfo?Kern, Takt, Temperatur, Name (system.status).≤ 5 µssystem.processor0x0002_000B
Beispiel
var index = 0
while let cpu = System.processor(index) { var name = TextLine(); cpu.appendName(to: &name); index += 1 }
Muster (kurz, nicht aus einem Programm kopiert)
System.cpuStatus() -> (cpus: Int, now: UInt64, idle: InlineArray<8, UInt64>)?Leerlaufzeit je CPU (system.status).≤ 5 µssystem.processorTimes0x0002_0006
Beispiel
if let status = System.cpuStatus() { /* status.idle[cpu], status.now */ }
aus apps/activity (gekürzt)
System.bootMeasurements() -> InlineArray<20, UInt64?>?Messungen des Kerns von diesem Start (Benchmark).≤ 5 µssystem.bootTimes0x0002_000C
Protokoll
System.log(_ text: StaticString) -> Bool / log(_ line: TextLine) -> BoolEine Zeile ins Systemprotokoll (höchstens 160 Bytes; system.log).≤ 5 µssystem.log0x0002_0003
Beispiel
System.log("reader: no window")
aus apps/reader (gekürzt)
System.log(_ text: UnsafeRawBufferPointer, level: LogLevel) -> BoolMit Stufe .debug/.info/.warning/.error (Activity › Log).≤ 5 µssystem.log0x0002_0003
Zeit
System.time: UInt64 / System.now() -> Nowns seit Start / Wanduhr (ns seit 1970), seit Start, Zeitzone.≤ 1 µssystem.now0x0002_0002
Beispiel
static var wall: Int64 { System.now().wall }
aus apps/develop (gekürzt)
System.wallClock: UInt64Millisekunden seit 1970 (0 solange unbekannt).≤ 1 µssystem.now0x0002_0002
System.adjustClock(byMilliseconds offset: Int64) -> UInt64?Uhr verstellen (system.time).≤ 5 µssystem.adjustClock0x0002_000A
Ablauf
System.sleep(nanoseconds: UInt64)Schlafen.sleep (Systemaufruf 2)
Beispiel
System.sleep(nanoseconds: 10_000_000)
aus apps/dialogcheck (gekürzt)
System.yield()Rechenzeit abgeben.yield (Systemaufruf 1)
System.exit(_ code: UInt64) -> NeverProgramm beenden.exit (Systemaufruf 0)
Beispiel
case .close: System.exit(0)
aus sdk-apps/greeter (gekürzt)
Zufall
System.random<let n: Int>(_ buffer: inout InlineArray<n, UInt8>) -> BoolZufallsbytes aus dem Generator des Kerns (Schlüssel, Nonces).≤ 5 µssystem.random0x0002_0009
Beispiel
var key = InlineArray<32, UInt8>(repeating: 0)
guard System.random(&key) else { return }
Muster (kurz, nicht aus einem Programm kopiert)
Strom
System.power(restart: Bool)Aus oder Neustart (system.power); kehrt nur bei Ablehnung zurück.ohne Budgetsystem.power0x0002_0004

AppsAndere Programme

AufrufWas er tutShape-Aufruf · Zeitbudget
Starten & stoppen
Apps.launch(_ name: Span<UInt8>) -> FileError?Systemprogramm oder App starten (apps.launch).≤ 50 msprograms.launch0x0006_0007
Beispiel
_ = callcost.withUTF8Buffer { Apps.launch(Span(_unsafeElements: $0)) }
aus apps/apicheck (gekürzt)
Apps.stop(_ name: Span<UInt8>) -> FileError?Laufendes Programm stoppen (Dienste nicht).≤ 50 µsprograms.stop0x0006_0008
Beispiel
return Apps.stop(name.span.extracting(0..<count)) == nil
aus apps/activity (gekürzt)
Apps.open(_ program: Span<UInt8>, document path: Span<UInt8>) -> FileError?Programm mit einem Dokument starten oder es ihm übergeben.≤ 50 msprograms.open0x0006_0009
Beispiel
return Apps.open(program.span, document: path.span) == nil
aus apps/files (gekürzt)
Dokument
Apps.document() -> (name: InlineArray<80, UInt8>, length: Int, generation: UInt64)?Das diesem Programm übergebene Dokument; generation ändert sich mit jedem neuen.≤ 5 µsprograms.document0x0006_0001
Beispiel
guard let document = Apps.document(), document.generation != generation else { return false }
aus apps/reader (gekürzt)
Listen
Apps.running(at index: Int) -> Running?Das index-te laufende Programm (Index 0 fragt neu).≤ 50 µsprograms.running0x0006_000A
Beispiel
while let program = Apps.running(at: index) { index += 1 }
aus apps/console (gekürzt)
Apps.installed(at index: Int) -> Running?Das index-te installierte Programm.≤ 20 msprograms.installed0x0006_000B
Beispiel
while let entry = Apps.installed(at: index) { index += 1 }
aus apps/display (gekürzt)
Running: name, id, version, kind, realtime, period, missed, worstLatency, onDemand, helper, seesCamera, hearsMicrophone, sendsWas ein Eintrag zeigt.
Running.hiddenFromDock neuInstalliert mit dock = no.
Apps.usage(at index: Int) -> Usage?Verbrauch: CPU-Zeit, Speicher, Heap, Fenster, Datei- und Netzverkehr … (system.status).≤ 100 µsprograms.usage0x0006_000E
Beispiel
while let usage = Apps.usage(at: index) { /* usage.cpuTime, usage.memory, usage.total */ index += 1 }
aus apps/activity (gekürzt)
Abstürze & Signatur
Apps.crash(at index: Int) -> Crash?Die letzten 8 Abstürze, neueste zuerst: name, reason, sequence, wallClock, pc, memoryLimit, reportPath.≤ 20 µsprograms.crashes0x0006_000D
Beispiel
while let crash = Apps.crash(at: index) { System.log(crash.reportPath); index += 1 }
Muster (kurz, nicht aus einem Programm kopiert)
Apps.signature(of name: Span<UInt8>) -> SignatureState?Signatur prüfen wie beim Start.≤ 50 msprograms.signature0x0006_000F
Apps.presentation(of name: Span<UInt8>, into buffer: UnsafeMutableRawPointer) -> Presentation?Titel, Gruppe und Symbol eines Programms (Dock).≤ 5 msprograms.presentation0x0006_000C
Löschen
Apps.uninstall(_ name: Span<UInt8>, deleteData: Bool, password: Span<UInt8>) -> TextLine?Programm aus /apps löschen (Passwort des Besitzers; files.system + apps.launch).≤ 3 sprograms.uninstall0x0006_0013
Apps.leftover(at:) / deleteLeftover(_:password:)Datenordner gelöschter Programme.≤ 50 msprograms.leftovers0x0006_0014
Protokoll lesen
Apps.logEnd / logLine(from:into:)Das Systemprotokoll mitlesen (system.log.read).≤ 20 µssystem.readLog0x0002_0005
Beispiel
var position = Apps.logEnd
var line = InlineArray<160, UInt8>(repeating: 0)
while let length = Apps.logLine(from: &position, into: &line) { /* line[0..<length] */ }
Muster (kurz, nicht aus einem Programm kopiert)

Updates (system.update)

AufrufWas er tutShape-Aufruf · Zeitbudget
Updates (system.update)
System.updateStatus() -> UpdateStatus?Welche Systemkopie (A/B) läuft, Probelauf, Rückfall, Versionen.≤ 5 µsupdate.status0x0008_0001
System.installUpdate(_ path: Span<UInt8>) -> UpdateResultSigniertes Paket in die ruhende Kopie installieren; startet nach dem Neustart.≤ 60 supdate.install0x0008_0002
System.setTextStart(_ text: Bool) -> Bool / setStartSound(_ on: Bool) -> BoolTextstart statt Animation / Startton.≤ 50 msupdate.textStart0x0008_0003

SystemdiensteNur Display-Dienst und Treiber

AufrufWas er tutShape-Aufruf · Zeitbudget
Nur Display-Dienst und Treiber
System.protectionStatus() / protectionSetUp / protectionCheck / ownerChoices()Geräteschutz und Besitzerwahl (system.protection).≤ 5 µsprotection.status0x0007_0001
System.openUSBController(_:) / openCANController(_:) / allocateDMA(_:) / waitForDevice(timeout:)Treiber: Register, DMA-Speicher, Interrupts (device.usb, device.can).≤ 1 msdevices.open0x0009_0001
System.submitInput(kind: UInt64, code: UInt64, value: UInt64)Eingabe-Ereignis liefern (input.provide).≤ 5 µsdisplay.submitInput0x0011_0006
Beispiel: Systeminformation, Einstellungen, ein Programm starten (apps/apicheck)
guard let info = System.info() else { fail("no system information"); return }
guard info.apiVersion == 1, info.version.count > 0, info.cpus >= 1 else { fail("system information looks wrong"); return }

let callcost: StaticString = "callcost"
_ = callcost.withUTF8Buffer { Apps.launch(Span(_unsafeElements: $0)) }
Beispiel: Die letzten Abstürze auflisten (apps.launch)
var index = 0
while let crash = Apps.crash(at: index) {
    var line = TextLine()
    for i in 0..<crash.nameLength { line.append(byte: crash.name[i]) }
    line.append(": ")
    line.append(crash.reportPath)
    System.log(line)
    index += 1
}

Zwischenablage

Text bis 64 KiB, eine für das ganze System. Jedes Programm mit Fenster darf kopieren; lesen darf es nur direkt nachdem der Besitzer in diesem Programm „Einsetzen“ gewählt hat (der Display-Dienst gibt einen Lesezugriff innerhalb von zwei Sekunden frei). Kein Programm kann die Zwischenablage beobachten. Das Standard-Bearbeiten-Menü (addEditMenu) und InputField erledigen das von selbst.

Clipboard

AufrufWas er tutShape-Aufruf · Zeitbudget
Kopieren
static copy(_ text: Span<UInt8>) -> BoolText (UTF-8, höchstens 64 KiB) in die Zwischenablage.≤ 50 µsclipboard.copy0x0012_0001
Beispiel
_ = Clipboard.copy(text.span.extracting(0..<length))
aus apps/console (gekürzt)
static var generation: UInt64Zähler, der mit jedem Kopieren wächst (die Mac-Brücke).≤ 2 µsclipboard.generation0x0012_0004
Einsetzen
static paste(into buffer: inout MutableSpan<UInt8>) -> IntLiest in buffer; 0 wenn leer oder gerade nicht freigegeben.≤ 50 µsclipboard.paste0x0012_0002
Beispiel
var buffer = [UInt8](repeating: 0, count: 64 * 1024)
var span = buffer.mutableSpan
let length = Clipboard.paste(into: &span)
aus apps/develop (gekürzt)
Freigeben (Dienst)
static grant(process slot: Int)Gibt Programm slot einen Lesezugriff (nur der Display-Dienst).ohne Budgetclipboard.grant0x0012_0003
Beispiel
if id == EditMenu.paste { Clipboard.grant(process: windows[window].info.owner) }
aus apps/display (gekürzt)
Beispiel: Kopieren und Einsetzen im Editor (apps/develop)
switch id {
case EditMenu.copy, EditMenu.cut:
    guard Editor.hasSelection else { return }
    let bytes = Editor.selectedBytes
    _ = bytes.withUnsafeBufferPointer { Clipboard.copy(Span(_unsafeElements: $0)) }
    if id == EditMenu.cut { Editor.insert([]) }
case EditMenu.paste:
    var buffer = [UInt8](repeating: 0, count: 64 * 1024)
    var span = buffer.mutableSpan
    let length = Clipboard.paste(into: &span)
    _ = span
    guard length > 0 else { return }
    Editor.insert(Array(buffer[0..<length]))
default: break
}

Kanäle & IPC

Mit ipc: Zwei Programme, die denselben Namen öffnen, teilen eine Seite mit einem Nachrichtenring in jede Richtung (Nachrichten bis 256 Bytes). Wecken und Warten sind primitive Systemaufrufe (channelNotify, channelWait). Läuft die Gegenseite auf einer anderen CPU, fragt receiveWaiting den Ring erst kurz ab – meist antwortet sie binnen Mikrosekunden. So reden auch Roboterprogramme mit dem Aktor-Dienst (actuator) und Treiber untereinander (CAN-Rahmen).

Channel

AufrufWas er tutShape-Aufruf · Zeitbudget
Öffnen
init?(_ name: StaticString)Kanal öffnen oder beitreten; nil ohne ipc.≤ 1 mschannels.open0x000A_0001
Beispiel
guard var channel = Channel("bench") else { System.log("ipcping: channel refused"); return }
aus apps/ipcping (gekürzt)
let handle: UInt64Nummer des Kanals.
Senden
send(_ message: Span<UInt8>) -> BoolNachricht einreihen (höchstens 256 Bytes); false bei vollem Ring.
Beispiel
while !channel.send(message.span) { System.yield() }
aus apps/ipcping (gekürzt)
send(_ frame: CANFrame) -> BoolEinen CAN-Rahmen senden.
mutating notify()Die Gegenseite wecken.channelNotify (Systemaufruf 8)
Beispiel
_ = actuator.send(bytes.span)
actuator.notify()
aus apps/arm (gekürzt)
Empfangen
receive(into out: inout InlineArray<256, UInt8>) -> Int?Älteste eingegangene Nachricht oder nil (wartet nicht).
Beispiel
while let count = actuator.receive(into: &message), count >= 13 { /* Antwort auswerten */ }
aus apps/arm (gekürzt)
receiveWaiting(into out: inout InlineArray<256, UInt8>) -> IntBlockiert, bis eine Nachricht kommt (verlorene Weckrufe abgefangen).channelWait (Systemaufruf 9)
Beispiel
let count = channel.receiveWaiting(into: &reply)
aus apps/ipcping (gekürzt)
receivePolling(into out: inout InlineArray<256, UInt8>, microseconds: UInt64 = 30) -> Int?Nächste Nachricht, wenn sie binnen µs kommt (nur bei Gegenseite auf anderer CPU).
Beispiel
if open.receivePolling(into: &answer) != nil { return true }
aus apps/benchmark (gekürzt)
wait(timeout: UInt64)Schläft bis zum Weckruf oder Ablauf; kann früher zurückkehren.channelWait (Systemaufruf 9)
Beispiel
channel!.wait(timeout: 50_000_000)
aus apps/develop (gekürzt)

SharedPage · SharedRingRohe geteilte Wörter

AufrufWas er tutShape-Aufruf · Zeitbudget
Rohe geteilte Wörter
SharedPage: load / store / loadAcquire / storeReleaseDie Kanalseite Wort für Wort (geordnete Zugriffe).
SharedRing: load32 / store32 / loadAcquire32 / storeRelease32 / barrierEine Veröffentlichung Wort für Wort.
Beispiel: Hin und zurück über den Kanal "bench" (apps/ipcping)
// manifest: capabilities = system.log, ipc
guard var channel = Channel("bench") else {
    System.log("ipcping: channel refused")
    return
}
var reply = InlineArray<256, UInt8>(repeating: 0)
var message = InlineArray<64, UInt8>(repeating: 0x42)
for round in 0..<2_000 {
    message[0] = UInt8(truncatingIfNeeded: round)
    while !channel.send(message.span) { System.yield() }
    channel.notify()
    let count = channel.receiveWaiting(into: &reply)
    guard count == 64, reply[0] == message[0] else { System.log("ipcping: wrong reply"); return }
}

Text, Zeit & Mathematik

Ohne Heap braucht es feste Bausteine: TextLine ist eine Zeile fester Größe (160 Bytes), WallClock rechnet Kalenderzeiten (Berlin), Maths liefert die Funktionen der Mathematik-Bibliothek für jedes Profil.

TextLine

AufrufWas er tutShape-Aufruf · Zeitbudget
Anlegen
TextLine() / TextLine(_ text: StaticString)Leere bzw. vorbelegte Zeile.
Anhängen
append(_ text: StaticString) / append(_ other: TextLine) / append(byte: UInt8)Anhängen.
append(decimal value: UInt64) / append(hex value: UInt64, minimumDigits: Int = 1)Zahlen anhängen.
Beispiel
var line = TextLine("ipcping: ")
line.append(decimal: rounds)
line.append(" round trips")
aus apps/ipcping (gekürzt)
Lesen
count / subscript(index:) / forEachByte(_:) / capacityLesen.

WallClock · CivilTime

AufrufWas er tutShape-Aufruf · Zeitbudget
Umrechnen
WallClock.civil(unixMilliseconds: Int64) -> CivilTimems seit 1970 → Jahr, Monat, Tag, Stunde, Minute, Sekunde, Millisekunde.
Beispiel
let local = WallClock.civil(unixMilliseconds: milliseconds + WallClock.berlinOffset(unixSeconds: milliseconds / 1000) * 1000)
aus apps/files (gekürzt)
WallClock.berlinOffset(unixSeconds: Int64) -> Int64Versatz der Berliner Zeit (mit Sommerzeit).
WallClock.unixSeconds(_ time: CivilTime) -> Int64 / days(year:month:day:)Rückrechnung.
Ausgeben
WallClock.appendDate(_ time: CivilTime, to line: inout TextLine) / appendTime(_:to:)"2026-11-10" / "14:23:05" anhängen.
Beispiel
WallClock.appendDate(local, to: &line)
aus apps/files (gekürzt)

Maths

AufrufWas er tutShape-Aufruf · Zeitbudget
Maths
sin, cos, tan, asin, acos, atan, atan2, sinCosTrigonometrie.
Beispiel
let out = p.x * Maths.cos(yaw) + p.y * Maths.sin(yaw)
aus apps/fpcheck (gekürzt)
sqrt, hypot, pow, exp, log, log2, log10Wurzeln, Potenzen, Logarithmen.
floor, ceil, round, trunc, fmod, radians, degreesRunden und Umrechnen.
pi, eKonstanten.
Beispiel: Eine Zeile mit Datum bauen
var line = TextLine("Stand ")
let time = WallClock.civil(unixMilliseconds: Int64(System.wallClock))
WallClock.appendDate(time, to: &line)
line.append(" ")
WallClock.appendTime(time, to: &line)
line.append(", Winkel ")
line.append(decimal: UInt64(Maths.degrees(Maths.atan2(1, 1))))
System.log(line)

Reflex (Develop)

Reflex ist die Sprache von Develop: Ein Ereignis rein, eine Reaktion raus. Ein Reflex-Programm ruft die Shape-Module direkt beim Namen (robot.move, vision.detections) – ohne Swift. Jede Zeile nennt ihr Modul; der Compiler sammelt daraus die Module und damit die Rechte, die beim Sichern als Programm in das signierte Manifest kommen. Von Develop gebaute Programme bekommen höchstens: Protokoll, Fenster, Ton, Kamera und Erkennungen, Mikrofon, den eigenen Ordner, Netz nur wenn erlaubt, den Roboter innerhalb seiner Grenzen über den Aktor-Dienst – nie Systemrechte.

Bausteine: state, on modul.ereignis(…) -> State, every 200 ms, realtime every 1 ms (Develop beweist die Worst-Case-Zeit, höchstens 80 % der Periode), emit modul.aufruf(…) für Effekte, func, screen { … }, test. Einheiten sind Teil der Typen (40°, 0.4 s, 880 Hz).

Ereignisse (on …)

AufrufWas er tutShape-Aufruf · Zeitbudget
Programm
on start / on stopProgramm startet / soll anhalten (letzte Gelegenheit zum Sichern).Ereignissystem.start0x0002_0007
every 200 msZeitgeber.
Sehen & Hören
on vision.detections(seen: Detections)Neue Erkennungen von Video.≤ 10 µsvision.detections0x0021_0001
on microphone.loud(heard: Loudness)Lauter als ein Pegel (Knall, blockierender Motor).Ereignismicrophone.loud0x0031_0008
on microphone.voice(change: VoiceChange)Sprechen begann oder endete.Ereignismicrophone.voice0x0031_0009
on camera.frame(…)Ein neues Kamerabild.≤ 10 µscamera.frame0x0020_0002
Roboter
on robot.moved(m: Motion)Eine Bewegung endete (erreicht, begrenzt oder gestoppt).Ereignisrobot.moved0x0101_0004
Fenster
on Name() (Knopf im screen)Ein Knopf des Fensters wurde gedrückt.Ereignisscreen.pressed0x0010_0003

Aufrufe & Effekte

AufrufWas er tutShape-Aufruf · Zeitbudget
Roboter
emit robot.move(joint:, to:, in:)Gelenk bewegen (über die Grenzen des Aktor-Dienstes).≤ 20 µsrobot.move0x0101_0001
robot.state(joint:)Zustand eines Gelenks (auch im Echtzeit-Handler, aus dem Ring).≤ 5 µsrobot.state0x0101_0002
arm.forward / arm.inverse / arm.jointsKinematik des Arms.≤ 20 µsarm.inverse0x0102_0002
Ton & Bild
emit sound.tone(pitch:, length:)Ein Ton.≤ 20 µssound.tone0x0030_0001
camera.toWorld(p)Bildpunkt → Weltpunkt.≤ 2 µscamera.toWorld0x0020_0003
Rechnen & Fragen
signal.spectrum / filter / stats / sineSpektren und Filter über Messreihen.≤ 500 µssignal.spectrum0x0040_0001
ask.claude(…)Claude fragen (mit dem Schlüssel des Besitzers aus Settings).ohne Budgetask.claude0x0050_0001
Fenster
screen { Title Text Big Button Slider Chart Picture Row Column }Fenster als Beschreibung; Aussehen kommt vom System.≤ 200 µsscreen.show0x0010_0001
Beispiel: Klatschen hält den Arm an (apps/develop/examples/clap-stop.reflex)
state Listener {
    bangs: Int32 = 0
    lastBang: Time = 0
    talking: Bool = false
}

/// A bang: hold the arm (every joint stays where it is) and beep.
on microphone.loud(heard: Loudness) -> Listener {
    if isBang(level: heard.peak) and heard.taken - lastBang > 1 s {
        emit robot.move(joint: 1, to: 0°, in: 2 s)
        emit sound.tone(pitch: 660 Hz, length: 150 ms)
        return self with { bangs: bangs + 1, lastBang: heard.taken }
    }
    return self
}

on microphone.voice(change: VoiceChange) -> Listener {
    if change.speaking and not talking {
        emit robot.move(joint: 5, to: 20°, in: 0.3 s)
    }
    return self with { talking: change.speaking }
}

func isBang(BangLevel { level: Float32 }) -> Bool { return level > -6 }

test "a clap is a bang" { expect isBang(level: -3) }
Beispiel: Echtzeit-Handler, von Develop bewiesen (apps/develop/guide/6-realtime.reflex)
state Hold {
    target: Angle = 30°
    periods: Int32 = 0
}

realtime every 1 ms -> Hold {
    let joint = robot.state(joint: 1)
    if periods % 20 == 0 {
        emit robot.move(joint: 1, to: joint.angle + step(gap: target - joint.angle).value, in: 20 ms)
    }
    return self with { periods: periods + 1 }
}

func step(Gap { gap: Angle }) -> Turn {
    if gap > 1° { return Turn(value: 1°) }
    if gap < -1° { return Turn(value: -1°) }
    return Turn(value: gap)
}

shape Turn { value: Angle }

Anhang: Systemaufrufe & Shape-Module

Unter der Swift-API liegen 14 primitive Systemaufrufe (enum SystemCall). Fast alles läuft über Nummer 13, den Shape-Aufruf: Anfrage und Antwort sind Shapes in fester Kodierung; die Aufruf-Kennung ist Modul << 16 | Aufrufnummer (z. B. files.open = 0x0003_0001). Der Kern prüft jede Anfrage gegen die Shape und die Rechte; das Budget ist die zugesagte längste Laufzeit. Quelle: shapes/*.shape.

SystemCallprimitive Systemaufrufe

NameWas er tutNummer
exitProgramm beenden0
yieldRechenzeit abgeben1
sleepSchlafen (ns)2
waitAuf Fenster/Dienst warten3
periodWaitEchtzeit: nächste Periode4
memoryGrowHeap vergrößern5
workerWakeWorker-Thread wecken6
workerExitWorker-Thread beenden7
channelNotifyKanal: Gegenseite wecken8
channelWaitKanal: warten9
deviceWaitTreiber: auf Interrupt warten10
surfaceNotifyFenster: Dienst wecken11
inputReceiveDisplay-Dienst: Eingaben abholen12
shapeCallShape-Aufruf: alle Module unten13

coreModul 0x0001 · Gemeinsame Shapes aller Module: Problem, Punkt, Drehung, Pose, Pixelrechteck.

Keine Aufrufe – nur gemeinsame Shapes.

systemModul 0x0002 · Der Roboter selbst: Info, Zeit, Protokoll, Strom, Zufall, Prozessoren, Worker-Threads; Ereignisse start/stop.

AufrufAnfrage → AntwortZeitbudget · Kennung
system.infoInfoRequest → SystemInfo≤ 5 µs0x0002_0001
system.nowNowRequest → Now≤ 1 µs0x0002_0002
system.logLogLine → Done≤ 5 µs0x0002_0003
system.powerPowerRequest → Doneohne Budget0x0002_0004
system.readLogLogRead → LogText≤ 20 µs0x0002_0005
system.processorTimesInfoRequest → ProcessorTimes≤ 5 µs0x0002_0006
system.randomRandomRequest → RandomBytes≤ 5 µs0x0002_0009
system.adjustClockClockAdjust → Now≤ 5 µs0x0002_000A
system.processorProcessorQuery → Processor≤ 5 µs0x0002_000B
system.bootTimesInfoRequest → BootTimes≤ 5 µs0x0002_000C
system.realtimeRealtimeQuery → RealtimeStats≤ 2 µs0x0002_000D
system.startWorkerWorkerStart → WorkerStarted≤ 1 ms0x0002_000E
system.start eventStartedEreignis0x0002_0007
system.stop eventStoppingEreignis0x0002_0008

filesModul 0x0003 · Dateien des Programms, Ordner, Volume und Snapshots.

AufrufAnfrage → AntwortZeitbudget · Kennung
files.openOpenFile → FileOpened≤ 2 ms0x0003_0001
files.readFileRead → FileData≤ 2 ms0x0003_0002
files.writeFileWrite → FileWritten≤ 2 ms0x0003_0003
files.closeFileRequest → Done≤ 5 ms0x0003_0004
files.listListFolder → FolderEntries≤ 2 ms0x0003_0005
files.infoFileQuery → FileInfo≤ 200 µs0x0003_0006
files.makeFolderFileQuery → Done≤ 2 ms0x0003_0007
files.removeFileQuery → Done≤ 2 ms0x0003_0008
files.renameRename → Done≤ 2 ms0x0003_0009
files.revealFileQuery → Doneohne Budget0x0003_000A
files.volumeVolumeQuery → VolumeInfo≤ 50 µs0x0003_000B
files.snapshotSnapshotQuery → SnapshotInfo≤ 50 µs0x0003_000C
files.takeSnapshotSnapshotQuery → SnapshotInfo≤ 50 ms0x0003_000D
files.deleteSnapshotSnapshotQuery → Done≤ 50 ms0x0003_000E
files.restoreRestoreFile → Restored≤ 2 s0x0003_000F

dbModul 0x0004 · SOFTrobotsDB: Datenbanken öffnen, schreiben, abfragen, Messreihen lesen.

AufrufAnfrage → AntwortZeitbudget · Kennung
db.openOpenDatabase → DatabaseOpened≤ 2 ms0x0004_0001
db.writeTableWrite → Written≤ 1 ms0x0004_0002
db.queryTableQuery → TablePage≤ 5 ms0x0004_0003
db.describeDatabaseRequest → Description≤ 1 ms0x0004_0004
db.closeDatabaseRequest → Done≤ 50 µs0x0004_0005
db.readSeriesSeriesRead → SeriesRecords≤ 5 ms0x0004_0006

netModul 0x0005 · Netzwerk: Namen, TCP, UDP, Lauschen (get/post geplant).

AufrufAnfrage → AntwortZeitbudget · Kennung
net.getGet → Response≤ 2 s0x0005_0001
net.postPost → Response≤ 2 s0x0005_0002
net.resolveResolve → Resolved≤ 6 s0x0005_0003
net.connectConnect → Connected≤ 5 s0x0005_0004
net.sendSocketSend → Sent≤ 1 s0x0005_0005
net.receiveSocketReceive → SocketData≤ 10 s0x0005_0006
net.closeSocketRequest → Done≤ 50 µs0x0005_0007
net.openDatagramDatagramOpen → DatagramOpened≤ 5 s0x0005_0008
net.sendDatagramDatagramSend → Done≤ 1 s0x0005_0009
net.receiveDatagramDatagramReceive → DatagramData≤ 10 s0x0005_000A
net.closeDatagramDatagramRequest → Done≤ 50 µs0x0005_000B
net.listenListen → Listening≤ 50 µs0x0005_000C
net.acceptAccept → Accepted≤ 10 s0x0005_000D
net.listenerInfoListenerRequest → ListenerInfo≤ 20 µs0x0005_000E
net.closeListenerListenerRequest → Done≤ 50 µs0x0005_000F

programsModul 0x0006 · Das Programm selbst und die anderen: Dokument, Settings, Geheimnis, Start/Stopp, Listen, Abstürze, Bauen.

AufrufAnfrage → AntwortZeitbudget · Kennung
programs.documentDocumentRequest → HandedDocument≤ 5 µs0x0006_0001
programs.settingsSettingsRequest → SettingsData≤ 200 µs0x0006_0002
programs.saveSettingsSettingsData → Done≤ 2 ms0x0006_0003
programs.setSecretSecretWrite → Done≤ 5 ms0x0006_0004
programs.hasSecretSecretQuery → SecretPresence≤ 2 ms0x0006_0005
programs.ownSecretSettingsRequest → SecretData≤ 2 ms0x0006_0006
programs.launchProgramName → Done≤ 50 ms0x0006_0007
programs.stopProgramName → Done≤ 50 µs0x0006_0008
programs.openOpenDocument → Done≤ 50 ms0x0006_0009
programs.runningProgramListQuery → RunningPrograms≤ 50 µs0x0006_000A
programs.installedProgramListQuery → InstalledPrograms≤ 20 ms0x0006_000B
programs.presentationProgramName → ProgramLook≤ 5 ms0x0006_000C
programs.crashesProgramListQuery → CrashList≤ 20 µs0x0006_000D
programs.usageProgramListQuery → UsageList≤ 100 µs0x0006_000E
programs.signatureProgramName → SignatureCheck≤ 50 ms0x0006_000F
programs.buildBuildProgram → BuiltProgram≤ 3 s0x0006_0010
programs.signShareSharePackage → ShareSignature≤ 20 ms0x0006_0011
programs.checkShareSharePackage → ShareCheck≤ 20 ms0x0006_0012
programs.uninstallUninstall → Done≤ 3 s0x0006_0013
programs.leftoversProgramListQuery → Leftovers≤ 50 ms0x0006_0014
programs.deleteLeftoverLeftoverDelete → Done≤ 3 s0x0006_0015

protectionModul 0x0007 · Geräteschutz: Passwort für DATA, Sperre, Besitzerwahl (system.protection).

AufrufAnfrage → AntwortZeitbudget · Kennung
protection.statusProtectionQuery → ProtectionStatus≤ 5 µs0x0007_0001
protection.setUpPassword → RecoveryKey≤ 3 s0x0007_0002
protection.unlockPassword → Done≤ 3 s0x0007_0003
protection.verifyPassword → Done≤ 3 s0x0007_0004
protection.choicesProtectionQuery → OwnerChoices≤ 2 ms0x0007_0005

updateModul 0x0008 · Systemkopien A/B und Updates (system.update).

AufrufAnfrage → AntwortZeitbudget · Kennung
update.statusUpdateQuery → UpdateState≤ 5 µs0x0008_0001
update.installUpdatePackage → Done≤ 60 s0x0008_0002
update.textStartStartLook → Done≤ 50 ms0x0008_0003
update.startSoundStartSound → Done≤ 50 ms0x0008_0004

devicesModul 0x0009 · Controller für Treiberprogramme (device.usb, device.can).

AufrufAnfrage → AntwortZeitbudget · Kennung
devices.openControllerQuery → Controller≤ 1 ms0x0009_0001
devices.allocateDMARequest → DMAMemory≤ 1 ms0x0009_0002

channelsModul 0x000A · Nachrichtenkanäle zwischen Programmen (ipc).

AufrufAnfrage → AntwortZeitbudget · Kennung
channels.openChannelName → ChannelOpened≤ 1 ms0x000A_0001

screenModul 0x0010 · Fenster als Beschreibung von Views (Reflex).

AufrufAnfrage → AntwortZeitbudget · Kennung
screen.showScreenView → Done≤ 200 µs0x0010_0001
screen.ScreenView effectEffekt0x0010_0002
screen.pressed eventPressedEreignis0x0010_0003
screen.slid eventSlidEreignis0x0010_0004
screen.key eventKeyPressEreignis0x0010_0005

displayModul 0x0011 · Bildschirm und Fensterspeicher (ui.surface, display.own).

AufrufAnfrage → AntwortZeitbudget · Kennung
display.openDisplayQuery → ScreenInfo≤ 5 ms0x0011_0001
display.flushPixelRect → Done≤ 200 µs0x0011_0002
display.screenshotDisplayQuery → Done≤ 50 ms0x0011_0003
display.createSurfaceDisplayQuery → SurfaceMemory≤ 5 ms0x0011_0004
display.surfaceSurfaceQuery → SurfaceState≤ 20 µs0x0011_0005
display.submitInputInputEvent → Done≤ 5 µs0x0011_0006

clipboardModul 0x0012 · Kopieren und Einsetzen.

AufrufAnfrage → AntwortZeitbudget · Kennung
clipboard.copyClipText → Done≤ 50 µs0x0012_0001
clipboard.pastePasteRequest → ClipText≤ 50 µs0x0012_0002
clipboard.grantPasteGrant → Doneohne Budget0x0012_0003
clipboard.generationPasteRequest → ClipGeneration≤ 2 µs0x0012_0004

dialogModul 0x0013 · Öffnen- und Sichern-Dialog des Systems (neu, 10.11.).

AufrufAnfrage → AntwortZeitbudget · Kennung
dialog.ask neuDialogAsk → DialogTicket≤ 1 ms0x0013_0001
dialog.result neuDialogTicket → DialogResult≤ 20 µs0x0013_0002
dialog.questions neuQuestionsRequest → DialogQuestions≤ 50 µs0x0013_0003
dialog.answer neuDialogAnswer → Done≤ 5 ms0x0013_0004

cameraModul 0x0020 · Die Kameras des Roboters (device.camera, camera.provide).

AufrufAnfrage → AntwortZeitbudget · Kennung
camera.listCameraListRequest → CameraList≤ 20 µs0x0020_0001
camera.frameFrameRequest → CameraFrame≤ 10 µs0x0020_0002
camera.toWorldPixelPoint → WorldPoint≤ 2 µs0x0020_0003
camera.fromWorldWorldPoint → PixelPoint≤ 2 µs0x0020_0004
camera.frame eventCameraFrameEreignis0x0020_0005
camera.ringCameraRingQuery → CameraRingMap≤ 1 ms0x0020_0006
camera.provideProvideCamera → ProvidedCamera≤ 1 ms0x0020_0007
camera.pushPushFrame → Done≤ 500 µs0x0020_0008
camera.closeCameraRingQuery → Done≤ 1 ms0x0020_0009

visionModul 0x0021 · Was die Kameras sehen: Erkennungen, Farbmasken, Bewegung, Marker.

AufrufAnfrage → AntwortZeitbudget · Kennung
vision.detectionsDetectionFilter → Detections≤ 10 µs0x0021_0001
vision.maskColorMask → Mask≤ 2 ms0x0021_0002
vision.motionMotionRequest → Motion2D≤ 1 ms0x0021_0003
vision.markersMarkerRequest → Markers≤ 3 ms0x0021_0004
vision.detections eventDetectionsEreignis0x0021_0005

soundModul 0x0030 · Ton über den Mischer des Systems (sound.play; tone/play/stop geplant).

AufrufAnfrage → AntwortZeitbudget · Kennung
sound.toneTone → Played≤ 20 µs0x0030_0001
sound.playPlay → Played≤ 100 µs0x0030_0002
sound.stopStopSound → Done≤ 20 µs0x0030_0003
sound.Tone effectEffekt0x0030_0004
sound.Play effectEffekt0x0030_0005
sound.openSoundQuery → SoundRing≤ 1 ms0x0030_0006
sound.closeSoundQuery → Done≤ 5 µs0x0030_0007
sound.statusSoundQuery → SoundStatus≤ 5 µs0x0030_0008
sound.setVolumeMasterVolume → Done≤ 50 µs0x0030_0009

microphoneModul 0x0031 · Die Mikrofone des Roboters (device.microphone, microphone.provide).

AufrufAnfrage → AntwortZeitbudget · Kennung
microphone.listMicrophoneListRequest → MicrophoneList≤ 20 µs0x0031_0001
microphone.openMicrophoneQuery → MicrophoneMap≤ 1 ms0x0031_0002
microphone.closeMicrophoneQuery → Done≤ 1 ms0x0031_0003
microphone.listenersListenersRequest → Listeners≤ 20 µs0x0031_0004
microphone.stopAllStopListening → Done≤ 1 ms0x0031_0005
microphone.provideProvideMicrophone → ProvidedMicrophone≤ 1 ms0x0031_0006
microphone.pushPushSamples → Done≤ 200 µs0x0031_0007
microphone.loud eventLoudnessEreignis0x0031_0008
microphone.voice eventVoiceChangeEreignis0x0031_0009

signalModul 0x0040 · Messwerte über die Zeit: Spektrum, Filter, Statistik.

AufrufAnfrage → AntwortZeitbudget · Kennung
signal.spectrumSpectrumRequest → Spectrum≤ 500 µs0x0040_0001
signal.filterFilterRequest → Samples≤ 200 µs0x0040_0002
signal.statsSamples → Stats≤ 50 µs0x0040_0003
signal.sineSineRequest → Samples≤ 2 ms0x0040_0004

askModul 0x0050 · Claude fragen (Reflex-Skripte mit dem Schlüssel aus Settings).

AufrufAnfrage → AntwortZeitbudget · Kennung
ask.claudeQuestion → Answerohne Budget0x0050_0001

safetyModul 0x0100 · Der Not-Halt (lesen frei, stop/release mit safety.stop).

AufrufAnfrage → AntwortZeitbudget · Kennung
safety.stateSafetyQuery → SafetyState≤ 2 µs0x0100_0001
safety.stopEmergencyStop → Done≤ 20 µs0x0100_0002
safety.releaseSafetyQuery → Done≤ 20 µs0x0100_0003

robotModul 0x0101 · Gelenke bewegen; Sicherheitsgrenzen und Not-Halt gelten immer.

AufrufAnfrage → AntwortZeitbudget · Kennung
robot.moveMove → Motion≤ 20 µs0x0101_0001
robot.stateJointQuery → JointState≤ 5 µs0x0101_0002
robot.Move effectEffekt0x0101_0003
robot.moved eventMotionEreignis0x0101_0004

armModul 0x0102 · Der Arm als Ganzes: Vorwärts-/Rückwärtskinematik, Gelenke, Drehmoment.

AufrufAnfrage → AntwortZeitbudget · Kennung
arm.forwardAngles → ToolPose≤ 2 µs0x0102_0001
arm.inverseReach → Angles≤ 20 µs0x0102_0002
arm.jointsJointsRequest → JointList≤ 10 µs0x0102_0003
arm.torqueTorqueCommand → Done≤ 1 µs0x0102_0004
arm.TorqueCommand effectEffekt0x0102_0005

peersModul 0x0110 · Roboter in der Nähe (für später reserviert).

AufrufAnfrage → AntwortZeitbudget · Kennung
peers.listPeersRequest → PeerListohne Budget0x0110_0001
peers.sendEnvelope → Deliveredohne Budget0x0110_0002
peers.received eventEnvelopeEreignis0x0110_0003

VollständigkeitAbgleich mit docs/api/app-api-1.txt

Alle 162 Typen der Liste kommen vor; rund 1000 der damals 1307 (heute 1946) Deklarationen sind einzeln mit Namen genannt. Bewusst nur zusammengefasst (Felder in einer Zeile) oder weggelassen ist, was ein Programm nicht direkt aufruft:

SurfaceMessage-Konstanten (das Draht-Protokoll zwischen Surface und Display-Dienst) · Layout-Offsets von DetectionRing · Einzelfelder von Usage, SystemStatus, UpdateStatus, ZipEntry, ProcessorInfo, FLACInfo, VP9Picture, UIDesign (Palette), Chart-Farben, MatroskaID-Konstanten · Interna von Deflater (Tabellen, Huffman) und HuffmanCode · die Ergebnis-Builder-Technik der Views (ViewBuilder, ViewList, ListAnd, ListOf1) · Hilfsprotokolle ByteSource, ByteSink, ZipDestination, ZipInput · Presentation.presentationIcon/-Bytes.

Umgekehrt zeigt diese Seite auch, was nicht in app-api-1.txt steht, weil tools/api/symbols.py die Ordner nicht liest: File, Files, FileError, Database und Verwandte (userland/shapes/), SecureConnection (uses-tls), Claude (uses-ai) – und das heute neue Running.hiddenFromDock.