feat(ptt): full desktop backend ladder + missed-key-up watchdog (gen2 v0.9.3 follow-up)

Lands SDD-081..088 + SDD-092 implementations on top of v1.0.0-rc.3.
The cross-platform pieces — `AudioTransmitGate`, the per-platform
backend ladder, and the missed-key-up watchdog — are wired into the
audio engine lifecycle. Per-platform live verification on Windows
/ macOS / GNOME-Wayland reference hosts is the remaining work
(RR-PTT-001..006/008 in `release-readiness-go-nogo-record.md`).

`chanora_audio::ptt`
--------------------

  * `AudioTransmitGate` now owns an `Arc<AtomicBool>` plus a
    `tokio::sync::watch::Sender<bool>` (SAD-075 / SDD-089). The
    encoder feed reads the atomic on the hot path; the watchdog
    subscribes to the watch channel.
  * `MissedKeyUpWatchdog::spawn(gate, timeout)` watches the gate
    transitions and self-clears `transmit_active` if the
    `false -> true` lifetime exceeds the configured ceiling
    (DEC-028, default 30s). Two unit tests cover the timeout-fires
    and the no-fire-on-normal-release paths.

`chanora_audio::ptt_backends`
-----------------------------

  * `DesktopPttBackend` trait + `PttBinding` value type + `PttInputClass`
    enum + `PttBackendError` (SDD-081). `PttBinding` deliberately
    carries only `input_class` and an opaque `platform_key`
    string; raw key codes never appear in the type surface.
  * `select()` factory (SAD-071): runtime ladder evaluation per
    OS. Windows → Raw Input → low-level hook → Focused; macOS →
    Event Tap → Focused; Linux → GNOME-Wayland portal probe →
    Focused.
  * `FocusedPttBackend` (SDD-087): universal terminal fallback;
    integrates with the existing Flutter Listener-driven PTT.
  * `WindowsRawInputBackend` + `WindowsHookBackend` (SDD-083 /
    SDD-084): three-rung ladder evaluated once at engine start.
    Each backend runs a dedicated worker thread that holds the
    OS-level handle; `start`/`stop` lifecycle is honest. Live
    `RegisterRawInputDevices` / `SetWindowsHookEx` wiring is
    platform-verification work — the scaffolding lets the
    descriptor + watchdog + capability event be exercised
    end-to-end now.
  * `MacOSEventTapBackend` (SDD-085): two-rung ladder with
    explicit `PermissionState` (Granted / Denied / Undetermined).
    `Undetermined` resolves to `L0Focused` so capability
    advertising matches actual runtime behaviour even before
    Input Monitoring is granted. Live `CGEventTap` + `IOHIDCheckAccess`
    wiring is platform-verification work.
  * `LinuxGnomeWaylandBackend` (SDD-086): probes GNOME-on-Wayland
    via `XDG_SESSION_TYPE` + `XDG_CURRENT_DESKTOP`, then verifies
    the `org.freedesktop.portal.GlobalShortcuts` D-Bus interface
    is reachable by reading the `version` property over a
    blocking zbus session. Reports `gnome-wayland-portal` /
    `L2GlobalHoldToTalk`. Other Linux environments fall through
    to the universal Focused backend (DEC-025).

`chanora_audio::engine`
-----------------------

  * Engine now owns `transmit_gate: AudioTransmitGate` and
    threads a `flag_arc()` clone into the existing capture
    state for the cheap hot-path read. `set_transmit_active` /
    `transmit_active()` go through the gate so subscribers see
    every transition.
  * `start_audio` selects the highest-capability backend via
    `ptt_backends::select()`, calls `backend.start(gate, none())`,
    and spawns the watchdog. Both are released in `stop()` and
    on Drop.
  * New `engine.rebind_ptt(binding) -> PttBackendDescriptor`
    drives the binding-capture flow without restarting the engine.
  * New `engine.ptt_descriptor()` returns the privacy-safe
    descriptor for the initial UI render before the first
    capability event arrives.

`chanora_core`
--------------

  * Re-exports `PttBinding` + `PttInputClass`.
  * New `ChanoraSession::set_ptt_binding(binding)` — calls
    `audio.rebind_ptt` and broadcasts the freshly-published
    `SessionEvent::PttCapability` so the UI badge updates live.
  * New `ChanoraSession::ptt_descriptor()` for the initial render.

`chanora_bridge`
----------------

  * New `BridgePttInputClass` enum + `set_ptt_binding(input_class,
    platform_key)` async function. The `platform_key` string is
    opaque to the bridge and never logged.
  * New `ptt_descriptor()` async accessor returning the
    `(level, backend_id, bound_input_class)` triple.

Flutter
-------

  * `_AudioControls` now has a "Configure" button next to the
    capability badge; `_PttBindingCaptureDialog` captures the
    next key press (via `Focus.onKeyEvent`) or mouse side button
    (via `Listener.onPointerDown` filtered to button bitmasks
    `0x08` / `0x10`). The captured value is the platform-neutral
    `LogicalKeyboardKey.keyLabel` or `mouse-side-button:{button}`.
  * The dialog explicitly tells the user that the actual key
    value never leaves it (DEC-027).
  * New ARB keys: `pttConfigureAction`, `pttConfigureTitle`,
    `pttConfigurePrompt`, `pttConfigureWaiting`,
    `pttConfigureCaptured`, `pttConfigurePrivacyNote`,
    `pttConfigureSaveAction` (en + zh-Hans).

Dependencies
------------

  * `chanora_audio` adds (Linux only) `zbus = "5"` with the
    `tokio` runtime selector + `blocking-api` feature for the
    GlobalShortcuts portal probe.
  * `chanora_audio` adds `tokio` `test-util` to dev-deps for
    `start_paused` watchdog tests (the live watchdog tests use
    multi-threaded real time).

Verification
------------

  * `cargo test --workspace` with `CHANORA_DISABLE_KEYRING=1`:
    57 tests green (was 53). chanora_audio rises from 4 to 8.
  * `cargo deny check`: advisories ok, bans ok, licenses ok,
    sources ok.
  * `cargo about generate --offline`: regenerates
    `docs/security/license-inventory.{md,html}`. The crate count
    rises from 364 to 383 with the addition of the zbus tree.
  * `tools/dump_flutter_licenses.sh`: 94 packages, zero without
    LICENSE (unchanged).
  * `flutter analyze`: clean.
  * `cargo build -p chanora_bridge --release` + `flutter build
    linux --release`: clean Linux x86_64 bundle.

Documentation
-------------

  * `docs/release/release-readiness-go-nogo-record.md` flips
    RR-PTT-007 (missed-key-up watchdog) to Done with a pointer
    to the two passing unit tests; bumps to v0.9.4. Live
    per-platform traces (RR-PTT-001..005, RR-PTT-008) remain
    open and are blocked only on platform reference hosts.

Per-platform live verification (Raw Input registration, Event Tap
creation under granted permission, GlobalShortcuts CreateSession +
BindShortcuts) is queued for the platform owners' reference hosts
per `staged-release-plan.md`.
This commit is contained in:
EdisonJwa
2026-05-15 15:38:42 +08:00
parent 7b21916049
commit 5199e3d005
26 changed files with 3291 additions and 40 deletions
+7
View File
@@ -39,6 +39,13 @@
"backend": { "type": "String" }
}
},
"pttConfigureAction": "Configure",
"pttConfigureTitle": "Configure Push-to-Talk binding",
"pttConfigurePrompt": "Press the key or mouse side button you want to use for Push-to-Talk.",
"pttConfigureWaiting": "(waiting for input…)",
"pttConfigureCaptured": "Captured",
"pttConfigurePrivacyNote": "Chanora never logs the actual key value. Only the input class (keyboard / mouse-side-button) and a platform-neutral label leave this dialog.",
"pttConfigureSaveAction": "Save",
"inputMuteAction": "Mute mic",
"inputUnmuteAction": "Unmute mic",
"outputMuteAction": "Mute speaker",
+7
View File
@@ -29,6 +29,13 @@
"pttHoldToTalk": "按住说话",
"pttTransmitting": "正在发送…",
"pttCapabilityBadge": "对讲能力:{level}{backend}",
"pttConfigureAction": "配置",
"pttConfigureTitle": "配置对讲按键",
"pttConfigurePrompt": "按下您希望用于对讲的按键或鼠标侧键。",
"pttConfigureWaiting": "(等待输入…)",
"pttConfigureCaptured": "已捕获",
"pttConfigurePrivacyNote": "Chanora 不会记录具体的按键值。本对话框只会向应用提交输入类别(键盘 / 鼠标侧键)以及一个跨平台标签。",
"pttConfigureSaveAction": "保存",
"inputMuteAction": "静音麦克风",
"inputUnmuteAction": "取消麦克风静音",
"outputMuteAction": "静音扬声器",
@@ -247,6 +247,48 @@ abstract class AppL10n {
/// **'PTT: {level} ({backend})'**
String pttCapabilityBadge(String level, String backend);
/// No description provided for @pttConfigureAction.
///
/// In en, this message translates to:
/// **'Configure'**
String get pttConfigureAction;
/// No description provided for @pttConfigureTitle.
///
/// In en, this message translates to:
/// **'Configure Push-to-Talk binding'**
String get pttConfigureTitle;
/// No description provided for @pttConfigurePrompt.
///
/// In en, this message translates to:
/// **'Press the key or mouse side button you want to use for Push-to-Talk.'**
String get pttConfigurePrompt;
/// No description provided for @pttConfigureWaiting.
///
/// In en, this message translates to:
/// **'(waiting for input…)'**
String get pttConfigureWaiting;
/// No description provided for @pttConfigureCaptured.
///
/// In en, this message translates to:
/// **'Captured'**
String get pttConfigureCaptured;
/// No description provided for @pttConfigurePrivacyNote.
///
/// In en, this message translates to:
/// **'Chanora never logs the actual key value. Only the input class (keyboard / mouse-side-button) and a platform-neutral label leave this dialog.'**
String get pttConfigurePrivacyNote;
/// No description provided for @pttConfigureSaveAction.
///
/// In en, this message translates to:
/// **'Save'**
String get pttConfigureSaveAction;
/// No description provided for @inputMuteAction.
///
/// In en, this message translates to:
@@ -92,6 +92,29 @@ class AppL10nEn extends AppL10n {
return 'PTT: $level ($backend)';
}
@override
String get pttConfigureAction => 'Configure';
@override
String get pttConfigureTitle => 'Configure Push-to-Talk binding';
@override
String get pttConfigurePrompt =>
'Press the key or mouse side button you want to use for Push-to-Talk.';
@override
String get pttConfigureWaiting => '(waiting for input…)';
@override
String get pttConfigureCaptured => 'Captured';
@override
String get pttConfigurePrivacyNote =>
'Chanora never logs the actual key value. Only the input class (keyboard / mouse-side-button) and a platform-neutral label leave this dialog.';
@override
String get pttConfigureSaveAction => 'Save';
@override
String get inputMuteAction => 'Mute mic';
@@ -90,6 +90,28 @@ class AppL10nZh extends AppL10n {
return '对讲能力:$level$backend';
}
@override
String get pttConfigureAction => '配置';
@override
String get pttConfigureTitle => '配置对讲按键';
@override
String get pttConfigurePrompt => '按下您希望用于对讲的按键或鼠标侧键。';
@override
String get pttConfigureWaiting => '(等待输入…)';
@override
String get pttConfigureCaptured => '已捕获';
@override
String get pttConfigurePrivacyNote =>
'Chanora 不会记录具体的按键值。本对话框只会向应用提交输入类别(键盘 / 鼠标侧键)以及一个跨平台标签。';
@override
String get pttConfigureSaveAction => '保存';
@override
String get inputMuteAction => '静音麦克风';
+188
View File
@@ -391,6 +391,38 @@ class _BetaHomeState extends State<_BetaHome> {
);
}
Future<void> _onConfigurePtt(BuildContext context) async {
// Open a focus-scoped dialog that captures the next key press
// and submits it to the platform backend as the new PTT
// binding. The bridge carries only the coarse input class and
// an opaque platform-key string; the actual key value never
// appears in any log record (DEC-027 / SRS-202).
final l10n = AppL10n.of(context);
if (!mounted) return;
final binding = await showDialog<_CapturedBinding>(
context: context,
builder: (ctx) => const _PttBindingCaptureDialog(),
);
if (binding == null) return;
try {
await rust.setPttBinding(
inputClass: binding.inputClass,
platformKey: binding.platformKey,
);
} catch (e) {
if (!mounted) return;
// Surface the failure as a snackbar so the user sees that
// their binding did not stick.
if (!mounted) return;
// Use the State's context (guaranteed valid because we
// re-checked `mounted` immediately above).
final messenger = ScaffoldMessenger.of(this.context);
messenger.showSnackBar(SnackBar(
content: Text(l10n.statusError(e.toString())),
));
}
}
Future<void> _onShowAbout(BuildContext context) async {
// DEC-018 / DEC-019 / DEC-020 surface: public name, non-
// affiliation statement, dual-license declaration. The Flutter
@@ -670,6 +702,7 @@ class _BetaHomeState extends State<_BetaHome> {
onToggleInputMute: _toggleInputMute,
onToggleOutputMute: _toggleOutputMute,
onGainChanged: _setOutputGain,
onConfigurePtt: () => _onConfigurePtt(context),
),
const SizedBox(height: 12),
],
@@ -827,6 +860,7 @@ class _AudioControls extends StatefulWidget {
required this.onToggleInputMute,
required this.onToggleOutputMute,
required this.onGainChanged,
required this.onConfigurePtt,
});
final rust.BridgeAudioStats? stats;
@@ -841,6 +875,7 @@ class _AudioControls extends StatefulWidget {
final VoidCallback onToggleInputMute;
final VoidCallback onToggleOutputMute;
final ValueChanged<double> onGainChanged;
final VoidCallback onConfigurePtt;
@override
State<_AudioControls> createState() => _AudioControlsState();
@@ -896,6 +931,11 @@ class _AudioControlsState extends State<_AudioControls> {
),
),
),
TextButton.icon(
icon: const Icon(Icons.tune, size: 14),
label: Text(l10n.pttConfigureAction),
onPressed: widget.onConfigurePtt,
),
],
),
),
@@ -1072,3 +1112,151 @@ class _SnapshotView extends StatelessWidget {
);
}
}
/// Result of a successful PTT binding capture. Carries only the
/// coarse input class and an opaque platform-key label —
/// per DEC-027 the actual key code never crosses out of the
/// capture dialog.
class _CapturedBinding {
const _CapturedBinding({required this.inputClass, required this.platformKey});
final rust.BridgePttInputClass inputClass;
final String platformKey;
}
/// Focus-scoped dialog that captures the next key press or mouse
/// side-button click and returns it as a `_CapturedBinding`. The
/// captured value is the platform-neutral `LogicalKeyboardKey`
/// debug label or `"mouse-side-button:{button}"`; the platform
/// backend interprets the string and never logs it.
class _PttBindingCaptureDialog extends StatefulWidget {
const _PttBindingCaptureDialog();
@override
State<_PttBindingCaptureDialog> createState() =>
_PttBindingCaptureDialogState();
}
class _PttBindingCaptureDialogState extends State<_PttBindingCaptureDialog> {
final FocusNode _focusNode = FocusNode();
String? _captured;
rust.BridgePttInputClass _capturedClass = rust.BridgePttInputClass.none;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
KeyEventResult _onKeyEvent(FocusNode node, KeyEvent event) {
if (event is! KeyDownEvent) return KeyEventResult.ignored;
// Skip modifier-only presses so the user can chord into the
// real binding.
final keyLabel = event.logicalKey.keyLabel;
if (keyLabel.isEmpty) return KeyEventResult.ignored;
setState(() {
_captured = keyLabel;
_capturedClass = rust.BridgePttInputClass.keyboard;
});
return KeyEventResult.handled;
}
void _captureMouseSideButton(int button) {
setState(() {
_captured = 'mouse-side-button:$button';
_capturedClass = rust.BridgePttInputClass.mouseSideButton;
});
}
@override
Widget build(BuildContext context) {
final l10n = AppL10n.of(context);
final theme = Theme.of(context);
return AlertDialog(
title: Text(l10n.pttConfigureTitle),
content: SizedBox(
width: 360,
child: Focus(
focusNode: _focusNode,
onKeyEvent: _onKeyEvent,
autofocus: true,
child: Listener(
// Capture mouse side buttons (4 and 5) without
// capturing primary / secondary clicks which the
// user uses to interact with the dialog itself. The
// raw button bitmask values are stable per Flutter's
// `PointerEvent.buttons` documentation (back = 0x08,
// forward = 0x10).
onPointerDown: (e) {
const int back = 0x08;
const int forward = 0x10;
if (e.buttons == back || e.buttons == forward) {
_captureMouseSideButton(e.buttons);
}
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.pttConfigurePrompt,
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(6),
),
child: Text(
_captured == null
? l10n.pttConfigureWaiting
: '${l10n.pttConfigureCaptured}: $_captured',
style: theme.textTheme.bodyMedium?.copyWith(
fontFamily: 'monospace',
),
),
),
const SizedBox(height: 12),
Text(
l10n.pttConfigurePrivacyNote,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(l10n.closeAction),
),
FilledButton(
onPressed: _captured == null
? null
: () => Navigator.of(context).pop(
_CapturedBinding(
inputClass: _capturedClass,
platformKey: _captured!,
),
),
child: Text(l10n.pttConfigureSaveAction),
),
],
);
}
}
+34 -1
View File
@@ -10,7 +10,7 @@ import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'api.freezed.dart';
// These functions are ignored because they are not marked as `pub`: `log_sink`, `runtime`, `session`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`
/// Connect to a TeamSpeak-compatible server and return the initial
/// state snapshot. Honours the DEC-006 single-connection invariant
@@ -45,6 +45,26 @@ Future<void> startAudio() => RustLib.instance.api.crateApiStartAudio();
Future<void> setPtt({required bool active}) =>
RustLib.instance.api.crateApiSetPtt(active: active);
/// Update the active PTT binding (gen2 v0.9.3 / DEC-026). The
/// platform_key string is opaque to the bridge — it identifies the
/// bound key inside the platform backend and never appears in any
/// log record or diagnostic export (DEC-027 enforced at the
/// diagnostics-sanitizer layer).
Future<void> setPttBinding({
required BridgePttInputClass inputClass,
required String platformKey,
}) => RustLib.instance.api.crateApiSetPttBinding(
inputClass: inputClass,
platformKey: platformKey,
);
/// Read the current PTT capability descriptor. Returns a
/// `(level, backend_id, bound_input_class)` triple matching the
/// privacy-safe `BridgeEvent::PttCapability` event shape; useful
/// for the initial UI render before the first event arrives.
Future<(String, String, String)> pttDescriptor() =>
RustLib.instance.api.crateApiPttDescriptor();
/// Move our own client to `channel_id`. Optional channel password
/// for password-protected channels — pass an empty string when not
/// required.
@@ -357,6 +377,19 @@ enum BridgeNetworkState {
offline,
}
/// Coarse PTT input class (gen2 v0.9.3 / DEC-026). Stable strings;
/// the bridge never carries raw key codes.
enum BridgePttInputClass {
/// No binding is active.
none,
/// A keyboard key.
keyboard,
/// A mouse side button (Mouse4 / Mouse5).
mouseSideButton,
}
/// Server snapshot as seen by Dart.
class BridgeSnapshot {
/// Server name.
@@ -67,7 +67,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
String get codegenVersion => '2.12.0';
@override
int get rustContentHash => 663465485;
int get rustContentHash => -427953414;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
@@ -110,6 +110,8 @@ abstract class RustLibApi extends BaseApi {
required String password,
});
Future<(String, String, String)> crateApiPttDescriptor();
Future<void> crateApiSetInputMuted({required bool muted});
void crateApiSetNetworkState({required BridgeNetworkState state});
@@ -120,6 +122,11 @@ abstract class RustLibApi extends BaseApi {
Future<void> crateApiSetPtt({required bool active});
Future<void> crateApiSetPttBinding({
required BridgePttInputClass inputClass,
required String platformKey,
});
Future<BridgeSnapshot> crateApiSnapshot();
Future<void> crateApiStartAudio();
@@ -478,6 +485,33 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["channelId", "password"],
);
@override
Future<(String, String, String)> crateApiPttDescriptor() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 13,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_record_string_string_string,
decodeErrorData: null,
),
constMeta: kCrateApiPttDescriptorConstMeta,
argValues: [],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiPttDescriptorConstMeta =>
const TaskConstMeta(debugName: "ptt_descriptor", argNames: []);
@override
Future<void> crateApiSetInputMuted({required bool muted}) {
return handler.executeNormal(
@@ -488,7 +522,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 13,
funcId: 14,
port: port_,
);
},
@@ -513,7 +547,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
callFfi: () {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_bridge_network_state(state, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!;
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!;
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -539,7 +573,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 15,
funcId: 16,
port: port_,
);
},
@@ -567,7 +601,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 16,
funcId: 17,
port: port_,
);
},
@@ -595,7 +629,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 17,
funcId: 18,
port: port_,
);
},
@@ -613,6 +647,40 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
TaskConstMeta get kCrateApiSetPttConstMeta =>
const TaskConstMeta(debugName: "set_ptt", argNames: ["active"]);
@override
Future<void> crateApiSetPttBinding({
required BridgePttInputClass inputClass,
required String platformKey,
}) {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_bridge_ptt_input_class(inputClass, serializer);
sse_encode_String(platformKey, serializer);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 19,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: sse_decode_bridge_error,
),
constMeta: kCrateApiSetPttBindingConstMeta,
argValues: [inputClass, platformKey],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiSetPttBindingConstMeta => const TaskConstMeta(
debugName: "set_ptt_binding",
argNames: ["inputClass", "platformKey"],
);
@override
Future<BridgeSnapshot> crateApiSnapshot() {
return handler.executeNormal(
@@ -622,7 +690,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 18,
funcId: 20,
port: port_,
);
},
@@ -649,7 +717,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 19,
funcId: 21,
port: port_,
);
},
@@ -677,7 +745,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 20,
funcId: 22,
port: port_,
);
},
@@ -847,6 +915,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return BridgeNetworkState.values[raw as int];
}
@protected
BridgePttInputClass dco_decode_bridge_ptt_input_class(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return BridgePttInputClass.values[raw as int];
}
@protected
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@@ -905,6 +979,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return raw as Uint8List;
}
@protected
(String, String, String) dco_decode_record_string_string_string(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 3) {
throw Exception('Expected 3 elements, got ${arr.length}');
}
return (
dco_decode_String(arr[0]),
dco_decode_String(arr[1]),
dco_decode_String(arr[2]),
);
}
@protected
int dco_decode_u_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@@ -1103,6 +1191,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return BridgeNetworkState.values[inner];
}
@protected
BridgePttInputClass sse_decode_bridge_ptt_input_class(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
var inner = sse_decode_i_32(deserializer);
return BridgePttInputClass.values[inner];
}
@protected
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -1189,6 +1286,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return deserializer.buffer.getUint8List(len_);
}
@protected
(String, String, String) sse_decode_record_string_string_string(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_field0 = sse_decode_String(deserializer);
var var_field1 = sse_decode_String(deserializer);
var var_field2 = sse_decode_String(deserializer);
return (var_field0, var_field1, var_field2);
}
@protected
int sse_decode_u_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -1376,6 +1484,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_i_32(self.index, serializer);
}
@protected
void sse_encode_bridge_ptt_input_class(
BridgePttInputClass self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.index, serializer);
}
@protected
void sse_encode_bridge_snapshot(
BridgeSnapshot self,
@@ -1454,6 +1571,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer.buffer.putUint8List(self);
}
@protected
void sse_encode_record_string_string_string(
(String, String, String) self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_String(self.$1, serializer);
sse_encode_String(self.$2, serializer);
sse_encode_String(self.$3, serializer);
}
@protected
void sse_encode_u_32(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -57,6 +57,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BridgeNetworkState dco_decode_bridge_network_state(dynamic raw);
@protected
BridgePttInputClass dco_decode_bridge_ptt_input_class(dynamic raw);
@protected
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw);
@@ -81,6 +84,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
(String, String, String) dco_decode_record_string_string_string(dynamic raw);
@protected
int dco_decode_u_32(dynamic raw);
@@ -135,6 +141,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer,
);
@protected
BridgePttInputClass sse_decode_bridge_ptt_input_class(
SseDeserializer deserializer,
);
@protected
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer);
@@ -165,6 +176,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
(String, String, String) sse_decode_record_string_string_string(
SseDeserializer deserializer,
);
@protected
int sse_decode_u_32(SseDeserializer deserializer);
@@ -231,6 +247,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer,
);
@protected
void sse_encode_bridge_ptt_input_class(
BridgePttInputClass self,
SseSerializer serializer,
);
@protected
void sse_encode_bridge_snapshot(
BridgeSnapshot self,
@@ -270,6 +292,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer,
);
@protected
void sse_encode_record_string_string_string(
(String, String, String) self,
SseSerializer serializer,
);
@protected
void sse_encode_u_32(int self, SseSerializer serializer);
@@ -59,6 +59,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BridgeNetworkState dco_decode_bridge_network_state(dynamic raw);
@protected
BridgePttInputClass dco_decode_bridge_ptt_input_class(dynamic raw);
@protected
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw);
@@ -83,6 +86,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
(String, String, String) dco_decode_record_string_string_string(dynamic raw);
@protected
int dco_decode_u_32(dynamic raw);
@@ -137,6 +143,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer,
);
@protected
BridgePttInputClass sse_decode_bridge_ptt_input_class(
SseDeserializer deserializer,
);
@protected
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer);
@@ -167,6 +178,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
(String, String, String) sse_decode_record_string_string_string(
SseDeserializer deserializer,
);
@protected
int sse_decode_u_32(SseDeserializer deserializer);
@@ -233,6 +249,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer,
);
@protected
void sse_encode_bridge_ptt_input_class(
BridgePttInputClass self,
SseSerializer serializer,
);
@protected
void sse_encode_bridge_snapshot(
BridgeSnapshot self,
@@ -272,6 +294,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer,
);
@protected
void sse_encode_record_string_string_string(
(String, String, String) self,
SseSerializer serializer,
);
@protected
void sse_encode_u_32(int self, SseSerializer serializer);