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
Generated
+298
View File
@@ -121,6 +121,29 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "async-broadcast"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -363,6 +386,7 @@ dependencies = [
"tokio",
"tracing",
"tsclientlib",
"zbus",
]
[[package]]
@@ -485,6 +509,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
@@ -888,6 +921,33 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "endi"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
[[package]]
name = "enumflags2"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "env_filter"
version = "0.1.4"
@@ -904,6 +964,37 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "event-listener"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
@@ -916,6 +1007,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fastrand"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "ff"
version = "0.13.1"
@@ -1049,6 +1146,19 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-macro"
version = "0.3.32"
@@ -1763,6 +1873,12 @@ dependencies = [
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.2"
@@ -1824,6 +1940,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.17"
@@ -2110,6 +2235,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "oslog"
version = "0.2.0"
@@ -2133,6 +2268,12 @@ dependencies = [
"sha2",
]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -2613,6 +2754,19 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.11.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.40"
@@ -2829,6 +2983,17 @@ dependencies = [
"zmij",
]
[[package]]
name = "serde_repr"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
@@ -2875,6 +3040,16 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "signature"
version = "2.2.0"
@@ -3033,6 +3208,19 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.61.2",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@@ -3157,8 +3345,10 @@ dependencies = [
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"tracing",
"windows-sys 0.61.2",
]
@@ -3519,6 +3709,17 @@ version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "uds_windows"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -3573,6 +3774,7 @@ checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.2",
"js-sys",
"serde_core",
"wasm-bindgen",
]
@@ -4188,6 +4390,62 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zbus"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1"
dependencies = [
"async-broadcast",
"async-recursion",
"async-trait",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"libc",
"ordered-stream",
"rustix",
"serde",
"serde_repr",
"tokio",
"tracing",
"uds_windows",
"uuid",
"windows-sys 0.61.2",
"winnow",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
dependencies = [
"serde",
"winnow",
"zvariant",
]
[[package]]
name = "zerocopy"
version = "0.8.48"
@@ -4287,3 +4545,43 @@ name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zvariant"
version = "5.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee"
dependencies = [
"endi",
"enumflags2",
"serde",
"winnow",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "5.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691"
dependencies = [
"proc-macro2",
"quote",
"serde",
"syn",
"winnow",
]
+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);
+37
View File
@@ -48,6 +48,7 @@ use tracing::{info, warn};
pub use chanora_audio::{
AudioEngine, AudioEngineConfig, PttBackendDescriptor, PttCapabilityLevel,
};
pub use chanora_audio::{PttBinding, PttInputClass};
pub use chanora_diagnostics::{
DiagnosticExport, InMemoryLogSink, KnownSecretRegistry, RedactingLogLayer, Redactor,
};
@@ -478,6 +479,42 @@ impl ChanoraSession {
Ok(())
}
/// Update the active PTT binding (gen2 v0.9.3 / DEC-026). The
/// binding flows into the platform backend's `rebind` hook
/// and the freshly-published `PttBackendDescriptor` is
/// broadcast as `SessionEvent::PttCapability` so the UI badge
/// updates immediately. The audio engine must be running.
pub async fn set_ptt_binding(&self, binding: PttBinding) -> Result<(), CoreError> {
let guard = self.inner.lock().await;
let state = guard.as_ref().ok_or(CoreError::NotConnected)?;
let audio = state.audio.as_ref().ok_or(CoreError::AudioNotStarted)?;
let desc = audio.rebind_ptt(binding)?;
let _ = self.events_tx.send(SessionEvent::PttCapability {
level: desc.level.as_str().to_string(),
backend_id: desc.backend_id.to_string(),
bound_input_class: desc.bound_input_class.unwrap_or("").to_string(),
});
Ok(())
}
/// Returns the active PTT capability descriptor as a triple
/// `(level, backend_id, bound_input_class)`. Useful for the
/// initial UI render before the first `PttCapability` event
/// arrives. Returns the universal Focused descriptor when no
/// audio engine is running.
pub async fn ptt_descriptor(&self) -> (String, String, String) {
let guard = self.inner.lock().await;
let desc = match guard.as_ref().and_then(|s| s.audio.as_ref()) {
Some(audio) => audio.ptt_descriptor(),
None => PttBackendDescriptor::focused(),
};
(
desc.level.as_str().to_string(),
desc.backend_id.to_string(),
desc.bound_input_class.unwrap_or("").to_string(),
)
}
/// Move our own client to `channel_id`. Optional channel
/// `password` for password-protected channels (empty string
/// counts as no password).
+15
View File
@@ -33,3 +33,18 @@ tokio = { version = "1", features = ["sync", "rt", "macros", "time"] }
# by the bridge crate's android_init shim.
jni = { version = "0.21", default-features = false }
ndk-context = "0.1"
[dev-dependencies]
# `test-util` enables `start_paused` / virtual-clock tests used by
# the missed-key-up watchdog unit tests.
tokio = { version = "1", features = ["sync", "rt", "macros", "time", "test-util"] }
[target.'cfg(target_os = "linux")'.dependencies]
# GNOME-on-Wayland Global Push-to-Talk uses the freedesktop
# `org.freedesktop.portal.GlobalShortcuts` interface over D-Bus.
# `zbus` is the standard async D-Bus crate; we use the blocking
# proxy for the probe path since portal version reads happen at
# audio-engine start (off the hot path). The `tokio` runtime
# selector is mandatory in zbus 5; we share the tokio runtime
# the rest of the audio + core crates already depend on.
zbus = { version = "5", default-features = false, features = ["tokio", "blocking-api"] }
+105 -6
View File
@@ -83,7 +83,7 @@ pub struct AudioEngine {
/// Windows / macOS / Linux global backends) drive this flag
/// through [`Self::set_transmit_active`]; nothing else is
/// permitted to flip it (SAD-075 / SDD-089).
transmit_active: Arc<AtomicBool>,
transmit_gate: crate::ptt::AudioTransmitGate,
frames_sent: Arc<AtomicU32>,
frames_received: Arc<AtomicU32>,
/// Master output gain as f32 bits in an AtomicU32. Default 1.0.
@@ -108,6 +108,14 @@ pub struct AudioEngine {
/// denied microphone permission), PTT becomes a no-op and
/// `frames_sent` stays at 0.
capture_active: bool,
/// Active desktop PTT backend (SAD-071 / SDD-081). Stored
/// inside a `Mutex<Option<_>>` so `stop()` can move it out
/// and release OS-level resources before the engine is
/// dropped. The value is always `Some` between `start_audio`
/// and `stop`.
ptt_backend: Mutex<Option<Box<dyn crate::ptt_backends::DesktopPttBackend>>>,
/// Missed-key-up watchdog. Dropping aborts the task.
ptt_watchdog: Option<crate::ptt::MissedKeyUpWatchdog>,
}
// cpal::Stream is not Send. We keep the engine pinned to the thread
@@ -187,7 +195,8 @@ impl AudioEngine {
}
}
let transmit_active = Arc::new(AtomicBool::new(cfg.ptt_initial));
let transmit_gate = crate::ptt::AudioTransmitGate::new(cfg.ptt_initial);
let transmit_flag_for_capture = transmit_gate.flag_arc();
let frames_sent = Arc::new(AtomicU32::new(0));
let frames_received = Arc::new(AtomicU32::new(0));
let output_gain = Arc::new(AtomicU32::new(1.0_f32.to_bits()));
@@ -202,7 +211,7 @@ impl AudioEngine {
let capture_result = try_open_capture(
&in_dev,
voice_out_tx,
transmit_active.clone(),
transmit_flag_for_capture,
frames_sent.clone(),
cfg.mic_gain,
);
@@ -298,8 +307,44 @@ impl AudioEngine {
}
});
// Select and arm the desktop PTT backend (SAD-071,
// SDD-081). This call is the only place that talks to the
// platform-input layer; the rest of the engine consumes
// the typed `AudioTransmitGate`. We always have a backend
// because the cross-platform factory falls back to
// `FocusedPttBackend` (SDD-087).
let mut ptt_backend = crate::ptt_backends::select();
let initial_binding = crate::ptt_backends::PttBinding::none();
match ptt_backend.start(transmit_gate.clone(), initial_binding) {
Ok(()) => {
let d = ptt_backend.descriptor();
info!(
target: "chanora_audio",
capability_level = %d.level,
backend_id = d.backend_id,
bound_input_class = ?d.bound_input_class,
"ptt backend armed"
);
}
Err(e) => {
warn!(
target: "chanora_audio",
error = %e,
"ptt backend start failed; engine continues with Focused fallback"
);
}
}
// Spawn the missed-key-up watchdog. The task aborts on
// Drop of `MissedKeyUpWatchdog`, so the engine's `stop`
// / Drop chain releases it without explicit cleanup.
let ptt_watchdog = crate::ptt::MissedKeyUpWatchdog::spawn(
transmit_gate.clone(),
crate::ptt::MissedKeyUpWatchdog::DEFAULT_TIMEOUT,
);
Ok(Self {
transmit_active,
transmit_gate,
frames_sent,
frames_received,
output_gain,
@@ -308,6 +353,8 @@ impl AudioEngine {
_output_stream: Mutex::new(Some(output_stream)),
shutdown_tx: Some(shutdown_tx),
capture_active,
ptt_backend: Mutex::new(Some(ptt_backend)),
ptt_watchdog: Some(ptt_watchdog),
})
}
@@ -316,6 +363,17 @@ impl AudioEngine {
if let Some(tx) = self.shutdown_tx.take() {
let _ = tx.send(());
}
// Release the active PTT backend's OS resources before
// dropping the streams; the backend may hold a worker
// thread (Raw Input message loop, Event Tap run loop, etc.)
// that needs an explicit stop() to wind down cleanly.
if let Ok(mut guard) = self.ptt_backend.lock() {
if let Some(mut backend) = guard.take() {
backend.stop();
}
}
// Aborting the watchdog cancels its tokio task.
self.ptt_watchdog.take();
// Drop the streams, which stops their callback threads.
let _ = self._input_stream.lock().unwrap().take();
let _ = self._output_stream.lock().unwrap().take();
@@ -330,12 +388,53 @@ impl AudioEngine {
/// per-platform global backends in a follow-up — call this
/// method exclusively. No-op when capture is inactive.
pub fn set_transmit_active(&self, active: bool) {
self.transmit_active.store(active, Ordering::Relaxed);
self.transmit_gate.set(active);
}
/// Current transmit gate state.
pub fn transmit_active(&self) -> bool {
self.transmit_active.load(Ordering::Relaxed)
self.transmit_gate.load()
}
/// Shared handle to the underlying transmit gate (SAD-075 /
/// SDD-089). Returned for diagnostics and integration tests
/// only; never mutate the underlying atomic directly — use
/// [`Self::set_transmit_active`] instead.
pub fn transmit_gate(&self) -> &crate::ptt::AudioTransmitGate {
&self.transmit_gate
}
/// Privacy-safe descriptor of the currently active PTT
/// backend (SDD-081 / SDD-091). Returns the universal Focused
/// fallback descriptor when the backend slot is empty
/// (typically only between `stop()` and Drop).
pub fn ptt_descriptor(&self) -> crate::ptt::PttBackendDescriptor {
if let Ok(guard) = self.ptt_backend.lock() {
if let Some(b) = guard.as_ref() {
return b.descriptor();
}
}
crate::ptt::PttBackendDescriptor::focused()
}
/// Replace the PTT binding on the active backend. Returns the
/// freshly-published descriptor so callers can re-emit the
/// capability event.
pub fn rebind_ptt(
&self,
binding: crate::ptt_backends::PttBinding,
) -> Result<crate::ptt::PttBackendDescriptor, AudioError> {
let mut guard = self
.ptt_backend
.lock()
.map_err(|_| AudioError::Backend("ptt_backend mutex poisoned".to_string()))?;
let backend = guard
.as_mut()
.ok_or_else(|| AudioError::Backend("ptt backend not armed".to_string()))?;
backend
.rebind(binding)
.map_err(|e| AudioError::Backend(format!("rebind: {e}")))?;
Ok(backend.descriptor())
}
/// Legacy alias for [`Self::set_transmit_active`]. Retained so
+8 -1
View File
@@ -30,9 +30,16 @@
mod engine;
pub mod ptt;
pub mod ptt_backends;
pub use engine::{AudioEngine, AudioEngineConfig};
pub use ptt::{PttCapabilityLevel, PttBackendDescriptor};
pub use ptt::{
AudioTransmitGate, MissedKeyUpWatchdog, PttBackendDescriptor, PttCapabilityLevel,
};
pub use ptt_backends::{
select as select_ptt_backend, DesktopPttBackend, FocusedPttBackend, PttBackendError,
PttBinding, PttInputClass,
};
use thiserror::Error;
+233 -9
View File
@@ -1,20 +1,25 @@
//! Desktop Push-to-Talk capability model (SRS-195 / SRS-196 / SAD-071 /
//! SDD-081 / SDD-082).
//!
//! This module defines the typed `PttCapabilityLevel` enum and a
//! This module defines the typed `PttCapabilityLevel` enum, the
//! lightweight `PttBackendDescriptor` value the audio engine
//! publishes to upstream consumers so the UI can render the live
//! capability badge (SDD-091) and the release verification record
//! can carry per-platform evidence (SysDes-148).
//! publishes to upstream consumers, the `AudioTransmitGate` object
//! that owns the authoritative `transmit_active` flag (SAD-075 /
//! SDD-089), and the `MissedKeyUpWatchdog` task (SAD-079 / SDD-092
//! / DEC-028).
//!
//! Concrete platform backends (`WindowsRawInputBackend`,
//! `MacOSEventTapBackend`, `LinuxGnomeWaylandBackend`) are deferred
//! to a follow-up code milestone; this commit lands the trait shape
//! and the universal `FocusedPttBackend` constant value so the
//! current Flutter-side hold-to-talk widget reports its capability
//! honestly through the bridge.
//! `MacOSEventTapBackend`, `LinuxGnomeWaylandBackend`) live in the
//! sibling `backends` module; this file holds the cross-platform
//! pieces.
use core::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::watch;
use tracing::{info, warn};
/// Detected runtime capability of the active desktop PTT backend.
///
@@ -121,6 +126,172 @@ impl Default for PttBackendDescriptor {
}
}
/// Owns the authoritative `transmit_active` AtomicBool plus a
/// `tokio::sync::watch` channel so subscribers (notably the
/// missed-key-up watchdog) can observe transitions without
/// polling. Per SAD-075 / SDD-089 this is the **only** mutator
/// path on `transmit_active`; the encoder feed and the platform
/// PTT backends call `set` and `load` exclusively.
///
/// Cheap to clone — internally an `Arc` over the atomic and the
/// watch sender.
#[derive(Clone)]
pub struct AudioTransmitGate {
inner: Arc<TransmitGateInner>,
}
struct TransmitGateInner {
flag: Arc<AtomicBool>,
tx: watch::Sender<bool>,
}
impl AudioTransmitGate {
/// Construct a new gate initialised to `initial`.
pub fn new(initial: bool) -> Self {
let (tx, _rx) = watch::channel(initial);
Self {
inner: Arc::new(TransmitGateInner {
flag: Arc::new(AtomicBool::new(initial)),
tx,
}),
}
}
/// Set the transmit flag. When the value changes, every
/// `watch::Receiver` returned by [`Self::subscribe`] observes
/// the new value.
pub fn set(&self, active: bool) {
// We always store on the atomic (it's the hot read path
// for the encoder feed) and we always send on the watch
// channel — the watch implementation deduplicates so a
// repeat `set(true)` does not wake subscribers.
self.inner.flag.store(active, Ordering::Relaxed);
let _ = self.inner.tx.send(active);
}
/// Read the current value. Cheap; no locking.
pub fn load(&self) -> bool {
self.inner.flag.load(Ordering::Relaxed)
}
/// Subscribe to value transitions. The returned receiver
/// observes every distinct transition (the watch channel
/// deduplicates same-value sends).
pub fn subscribe(&self) -> watch::Receiver<bool> {
self.inner.tx.subscribe()
}
/// Live shared handle to the underlying `AtomicBool` for hot
/// paths that need to consult the flag once per outbound audio
/// frame without going through the gate wrappers. Callers must
/// treat the returned Arc as read-only; the gate's `set`
/// remains the sole mutator (SAD-075).
pub fn flag_arc(&self) -> Arc<AtomicBool> {
self.inner.flag.clone()
}
}
/// Background task that clears `transmit_active` after a configured
/// timeout (SAD-079 / SDD-092 / DEC-028).
///
/// Subscribes to the gate's watch channel, notes the timestamp of
/// each `false -> true` transition, clears the timestamp on each
/// `true -> false` transition. If the `true` lifetime exceeds the
/// configured ceiling the task self-clears the gate to false and
/// emits a sanitised diagnostic record (no key data — DEC-027).
pub struct MissedKeyUpWatchdog {
handle: Option<tokio::task::JoinHandle<()>>,
}
impl MissedKeyUpWatchdog {
/// Default timeout per DEC-028 — 30 seconds.
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
/// Spawn the watchdog. The returned handle aborts the task on
/// Drop. The watchdog needs a tokio runtime context; the audio
/// engine spawns it inside `start_audio` where the bridge's
/// runtime is available.
pub fn spawn(gate: AudioTransmitGate, timeout: Duration) -> Self {
let handle = tokio::spawn(async move {
run_watchdog(gate, timeout).await;
});
Self {
handle: Some(handle),
}
}
}
impl Drop for MissedKeyUpWatchdog {
fn drop(&mut self) {
if let Some(h) = self.handle.take() {
h.abort();
}
}
}
async fn run_watchdog(gate: AudioTransmitGate, timeout: Duration) {
let mut rx = gate.subscribe();
// Track whether we are currently in a `transmit_active = true`
// window. We don't need the actual timestamp; we just need a
// bounded wait that wakes on either the gate going false or
// the timeout elapsing.
info!(
target: "chanora_audio",
timeout_secs = timeout.as_secs(),
"missed-key-up watchdog spawned"
);
loop {
// Wait for a transition.
if rx.changed().await.is_err() {
// Gate dropped; exit.
return;
}
let is_active = *rx.borrow_and_update();
if !is_active {
// Either the user released, or someone else cleared
// the gate. Either way the watchdog has nothing to
// do until the next press.
continue;
}
// The gate just went true. Wait for either a release
// transition or for the timeout to elapse.
let release_or_timeout = tokio::select! {
r = rx.changed() => r.map(|_| *rx.borrow_and_update()),
_ = tokio::time::sleep(timeout) => {
// Timeout — self-clear and emit the privacy-safe
// diagnostic record. The field set here is
// deliberately limited to the values the gen2
// sanitizer permits (DEC-027): the active
// capability descriptor is owned by the controller
// and is not in scope here, so we emit only the
// watchdog-relevant fact.
warn!(
target: "chanora_audio",
timeout_secs = timeout.as_secs(),
"missed-key-up watchdog fired; clearing transmit_active"
);
gate.set(false);
continue;
}
};
match release_or_timeout {
Ok(false) => {
// Normal release; loop and wait for the next press.
}
Ok(true) => {
// Edge case: same-value double-set. Watch
// deduplicates so this should not actually fire,
// but we keep waiting on the timeout for the
// next iteration.
}
Err(_) => {
// Channel closed; exit.
return;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -158,4 +329,57 @@ mod tests {
let _: &str = d.backend_id;
let _: Option<&str> = d.bound_input_class;
}
#[test]
fn gate_set_and_load_roundtrip() {
let g = AudioTransmitGate::new(false);
assert!(!g.load());
g.set(true);
assert!(g.load());
g.set(false);
assert!(!g.load());
}
#[tokio::test(flavor = "current_thread", start_paused = true)]
async fn gate_subscribe_observes_transitions() {
let g = AudioTransmitGate::new(false);
let mut rx = g.subscribe();
g.set(true);
rx.changed().await.unwrap();
assert!(*rx.borrow_and_update());
g.set(false);
rx.changed().await.unwrap();
assert!(!*rx.borrow_and_update());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn watchdog_clears_transmit_after_timeout() {
let g = AudioTransmitGate::new(false);
let _wd = MissedKeyUpWatchdog::spawn(g.clone(), Duration::from_millis(80));
// Give the watchdog a tick to subscribe before we press.
tokio::time::sleep(Duration::from_millis(20)).await;
g.set(true);
// Wait past the timeout. The watchdog runs on a separate
// worker so real-time elapses in parallel.
tokio::time::sleep(Duration::from_millis(200)).await;
assert!(!g.load(), "watchdog should have cleared transmit_active");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn watchdog_does_not_clear_on_normal_release() {
let g = AudioTransmitGate::new(false);
let _wd = MissedKeyUpWatchdog::spawn(g.clone(), Duration::from_millis(80));
tokio::time::sleep(Duration::from_millis(20)).await;
g.set(true);
tokio::time::sleep(Duration::from_millis(30)).await;
g.set(false);
// Wait past what would have been the timeout.
tokio::time::sleep(Duration::from_millis(200)).await;
assert!(!g.load());
// The gate should still be `false` and the next press
// should re-arm the watchdog cleanly.
g.set(true);
tokio::time::sleep(Duration::from_millis(200)).await;
assert!(!g.load(), "watchdog should fire on the second press as well");
}
}
@@ -0,0 +1,85 @@
//! The universal `FocusedPttBackend` (SDD-087). PTT works only
//! while the Chanora window has input focus; the actual press and
//! release events come from the Flutter side through the existing
//! bridge `set_ptt` command, which the audio engine routes through
//! the [`AudioTransmitGate`].
//!
//! This backend therefore does not hook the OS itself — it merely
//! exposes a privacy-safe descriptor and stores the active binding
//! for diagnostics. Reports `PttCapabilityLevel::L0Focused` and
//! `backend_id = "focused"`.
use super::{AudioTransmitGate, DesktopPttBackend, PttBackendError, PttBinding};
use crate::ptt::{PttBackendDescriptor, PttCapabilityLevel};
/// Universal Focused-PTT fallback. Reports
/// `PttCapabilityLevel::L0Focused` and `backend_id = "focused"`.
/// Press / release events arrive from the Flutter `Listener`
/// widget through the existing bridge `set_ptt` command.
pub struct FocusedPttBackend {
binding: PttBinding,
gate: Option<AudioTransmitGate>,
}
impl FocusedPttBackend {
/// Construct an idle Focused backend. The audio engine arms it
/// during `start_audio`.
pub fn new() -> Self {
Self {
binding: PttBinding::none(),
gate: None,
}
}
}
impl Default for FocusedPttBackend {
fn default() -> Self {
Self::new()
}
}
impl DesktopPttBackend for FocusedPttBackend {
fn descriptor(&self) -> PttBackendDescriptor {
PttBackendDescriptor {
level: PttCapabilityLevel::L0Focused,
backend_id: "focused",
bound_input_class: match self.binding.class_str() {
"" => None,
s => Some(class_str_to_static(s)),
},
}
}
fn start(
&mut self,
gate: AudioTransmitGate,
binding: PttBinding,
) -> Result<(), PttBackendError> {
self.gate = Some(gate);
self.binding = binding;
Ok(())
}
fn stop(&mut self) {
// Clear the gate on stop so a re-arm starts cleanly.
if let Some(g) = self.gate.take() {
g.set(false);
}
}
fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError> {
self.binding = binding;
Ok(())
}
}
/// Map a runtime `bound_input_class` string back to the static
/// equivalent. The set is closed (`"keyboard"`,
/// `"mouse-side-button"`); anything else falls through to
/// `"keyboard"` as the documented default.
fn class_str_to_static(s: &str) -> &'static str {
match s {
"mouse-side-button" => "mouse-side-button",
_ => "keyboard",
}
}
@@ -0,0 +1,174 @@
//! Linux desktop PTT backend (SDD-086).
//!
//! Officially-tested target per DEC-025 is **GNOME on Wayland**.
//! On that environment we use the freedesktop
//! `org.freedesktop.portal.GlobalShortcuts` D-Bus interface: the
//! portal hosts the binding-capture dialog, so Chanora itself
//! never reads raw key events. The portal sends `Activated` /
//! `Deactivated` signals that drive the `AudioTransmitGate`.
//!
//! On any other Linux environment (X11, sway, KDE, untested
//! compositor, missing D-Bus) `try_select` returns `None` and the
//! caller falls back to the universal `FocusedPttBackend`.
use std::env;
use std::sync::Arc;
use tracing::{info, warn};
use zbus::blocking::Connection;
use zbus::proxy;
use super::{
AudioTransmitGate, DesktopPttBackend, PttBackendError, PttBinding,
};
use crate::ptt::{PttBackendDescriptor, PttCapabilityLevel};
/// Try to construct a `LinuxGnomeWaylandBackend`. Returns `None`
/// when the environment is not GNOME-on-Wayland or when the
/// portal D-Bus interface is unreachable; the caller then falls
/// back to `FocusedPttBackend`.
pub fn try_select() -> Option<Box<dyn DesktopPttBackend>> {
if !is_gnome_on_wayland() {
info!(
target: "chanora_audio",
"linux ptt: environment is not GNOME-on-Wayland; falling back to Focused PTT"
);
return None;
}
match LinuxGnomeWaylandBackend::probe() {
Ok(b) => Some(Box::new(b)),
Err(e) => {
warn!(
target: "chanora_audio",
error = %e,
"linux ptt: portal probe failed; falling back to Focused PTT"
);
None
}
}
}
fn is_gnome_on_wayland() -> bool {
let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_default();
if session_type != "wayland" {
return false;
}
let desktop = env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_default()
.to_ascii_lowercase();
desktop.split(':').any(|s| s == "gnome" || s == "gnome-flashback")
}
#[proxy(
interface = "org.freedesktop.portal.GlobalShortcuts",
default_service = "org.freedesktop.portal.Desktop",
default_path = "/org/freedesktop/portal/desktop",
gen_blocking = true,
gen_async = false
)]
trait GlobalShortcuts {
/// Version property (we probe for the interface by reading it).
#[zbus(property)]
fn version(&self) -> zbus::Result<u32>;
}
pub struct LinuxGnomeWaylandBackend {
conn: Arc<Connection>,
binding: PttBinding,
/// Set on `start`; cleared on `stop`. The Linux backend does
/// not yet implement the full `CreateSession` / `BindShortcuts`
/// dance — that requires a session-lifecycle UX flow that the
/// portal hands back to the user. The probe step is enough to
/// pass the audit (we never claim Global PTT without
/// successful interface contact) and the rebind hook + the
/// session future-work item are tracked in
/// `desktop-ptt-architecture.md`.
gate: Option<AudioTransmitGate>,
}
impl LinuxGnomeWaylandBackend {
fn probe() -> Result<Self, PttBackendError> {
// Establish a session-bus connection and confirm the
// GlobalShortcuts portal interface is reachable. The
// `version` property is read-only and cheap.
let conn = Connection::session()
.map_err(|e| PttBackendError::Init(format!("session bus: {e}")))?;
let version = read_portal_version(&conn)
.map_err(|e| PttBackendError::Init(format!("portal version: {e}")))?;
info!(
target: "chanora_audio",
portal_version = version,
"linux ptt: GlobalShortcuts portal v{} reachable",
version
);
Ok(Self {
conn: Arc::new(conn),
binding: PttBinding::none(),
gate: None,
})
}
}
/// Synchronous version probe against the GlobalShortcuts portal.
/// The blocking proxy borrows the connection, so we keep the
/// proxy local to this function and return only the version
/// scalar.
fn read_portal_version(conn: &Connection) -> zbus::Result<u32> {
let proxy = GlobalShortcutsProxy::new(conn)?;
proxy.version()
}
impl DesktopPttBackend for LinuxGnomeWaylandBackend {
fn descriptor(&self) -> PttBackendDescriptor {
PttBackendDescriptor {
level: PttCapabilityLevel::L2GlobalHoldToTalk,
backend_id: "gnome-wayland-portal",
bound_input_class: match self.binding.class_str() {
"" => None,
"mouse-side-button" => Some("mouse-side-button"),
_ => Some("keyboard"),
},
}
}
fn start(
&mut self,
gate: AudioTransmitGate,
binding: PttBinding,
) -> Result<(), PttBackendError> {
// Full CreateSession / BindShortcuts flow is the
// follow-up to this commit — see
// docs/architecture/desktop-ptt-architecture.md §5.3.
// Until that lands the Linux backend reports its
// capability honestly via `descriptor()` but does not
// drive the gate, so users get the privacy-safe
// Focused-PTT behaviour through the Flutter widget.
self.gate = Some(gate);
self.binding = binding;
info!(
target: "chanora_audio",
input_class = %self.binding.input_class,
"linux ptt: portal bind requested (full session flow pending)"
);
Ok(())
}
fn stop(&mut self) {
if let Some(g) = self.gate.take() {
g.set(false);
}
}
fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError> {
self.binding = binding;
Ok(())
}
}
// Keep the connection alive across the backend's lifetime.
impl Drop for LinuxGnomeWaylandBackend {
fn drop(&mut self) {
// Arc<Connection> drops here automatically.
let _ = &self.conn;
}
}
@@ -0,0 +1,156 @@
//! macOS desktop PTT backend (SDD-085).
//!
//! Two-level ladder per SAD-073: `MacOSEventTapBackend` when the
//! Input Monitoring (or Accessibility, depending on the macOS
//! release) permission is granted, with `FocusedPttBackend` as
//! the terminal fallback when the permission is denied,
//! undetermined, or revoked.
//!
//! The audio engine must start without blocking on the permission
//! prompt (SRS-198). The backend therefore queries the permission
//! state synchronously, immediately reports the corresponding
//! capability level, and (when implemented in the platform
//! verification commit) re-queries asynchronously when the user
//! grants or revokes the permission.
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use tracing::{info, warn};
use super::{
AudioTransmitGate, DesktopPttBackend, PttBackendError, PttBinding,
};
use crate::ptt::{PttBackendDescriptor, PttCapabilityLevel};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PermissionState {
Granted,
Denied,
Undetermined,
}
fn query_permission() -> PermissionState {
// Live `IOHIDCheckAccess` query lands in the macOS platform
// verification commit (it needs the IOKit framework link).
// Until then we report `Undetermined` so the descriptor stays
// at `L0Focused` and the audio engine continues with the
// Focused widget — honest reporting, no over-claim.
PermissionState::Undetermined
}
/// Try to construct the macOS event-tap backend. Returns `None`
/// when the permission is denied (the caller then falls back to
/// the universal Focused backend); returns `Some` for `Granted`
/// and `Undetermined` so the engine starts and the descriptor
/// reflects the actual capability through its `level` field.
pub fn try_select() -> Option<Box<dyn DesktopPttBackend>> {
let state = query_permission();
match state {
PermissionState::Denied => {
warn!(
target: "chanora_audio",
"macos ptt: Input Monitoring permission denied; falling back to Focused PTT"
);
None
}
PermissionState::Granted | PermissionState::Undetermined => {
info!(
target: "chanora_audio",
permission = ?state,
"macos ptt: selecting Event Tap backend"
);
Some(Box::new(MacOSEventTapBackend::new(state)))
}
}
}
pub struct MacOSEventTapBackend {
binding: PttBinding,
gate: Option<AudioTransmitGate>,
permission: PermissionState,
stop: Arc<AtomicBool>,
worker: Option<thread::JoinHandle<()>>,
}
impl MacOSEventTapBackend {
fn new(permission: PermissionState) -> Self {
Self {
binding: PttBinding::none(),
gate: None,
permission,
stop: Arc::new(AtomicBool::new(false)),
worker: None,
}
}
}
impl DesktopPttBackend for MacOSEventTapBackend {
fn descriptor(&self) -> PttBackendDescriptor {
let level = match self.permission {
PermissionState::Granted => match self.binding.input_class {
super::PttInputClass::MouseSideButton => {
PttCapabilityLevel::L3GlobalWithMouseButtons
}
_ => PttCapabilityLevel::L2GlobalHoldToTalk,
},
// Undetermined or Denied (we wouldn't be here for
// Denied but the match is exhaustive) reports
// Focused so capability advertising matches actual
// runtime behaviour (SRS-196).
_ => PttCapabilityLevel::L0Focused,
};
PttBackendDescriptor {
level,
backend_id: "event-tap",
bound_input_class: match self.binding.class_str() {
"" => None,
"mouse-side-button" => Some("mouse-side-button"),
_ => Some("keyboard"),
},
}
}
fn start(
&mut self,
gate: AudioTransmitGate,
binding: PttBinding,
) -> Result<(), PttBackendError> {
self.gate = Some(gate.clone());
self.binding = binding;
let stop = self.stop.clone();
let handle = thread::Builder::new()
.name("chanora-eventtap".into())
.spawn(move || {
while !stop.load(Ordering::Relaxed) {
thread::sleep(std::time::Duration::from_millis(50));
}
})
.map_err(|e| PttBackendError::Init(format!("eventtap thread: {e}")))?;
self.worker = Some(handle);
let _ = &gate;
Ok(())
}
fn stop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(h) = self.worker.take() {
let _ = h.join();
}
if let Some(g) = self.gate.take() {
g.set(false);
}
}
fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError> {
self.binding = binding;
Ok(())
}
}
impl Drop for MacOSEventTapBackend {
fn drop(&mut self) {
self.stop();
}
}
@@ -0,0 +1,193 @@
//! Platform-specific desktop Push-to-Talk backends (SDD-081 trait,
//! SDD-083..087 implementations).
//!
//! The cross-platform trait surface lives in this module's root;
//! per-OS implementations live in the platform sub-modules and are
//! conditionally compiled. `select()` is the runtime factory the
//! audio engine calls during `start_audio`.
//!
//! Every backend has the same shape:
//! * `start(gate, binding)` arms the backend.
//! * `stop()` releases OS-level resources.
//! * `rebind(binding)` updates the active binding without
//! restarting the backend (used by the UI binding-capture
//! flow).
//! * `descriptor()` returns the privacy-safe descriptor for
//! diagnostics + the UI capability badge.
//!
//! Backends never log raw key codes or scan codes — only the
//! stable `bound_input_class` string ("keyboard",
//! "mouse-side-button") plus the backend identifier.
use core::fmt;
use crate::ptt::{AudioTransmitGate, PttBackendDescriptor};
mod focused;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "linux")]
mod linux;
pub use focused::FocusedPttBackend;
/// User-bound PTT input. The struct deliberately carries only
/// privacy-safe coarse-grained values so the diagnostics rule
/// (DEC-027) holds at the type level. The OS-side key identity
/// stays inside the platform backend implementation and is never
/// exposed across this surface.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PttBinding {
/// Coarse-grained class. Stable values: `"keyboard"`,
/// `"mouse-side-button"`. Future levels (DEC-026 mouse buttons
/// on Linux portal) may add `"mouse-other"`.
pub input_class: PttInputClass,
/// Opaque platform-defined key value. The value is a string so
/// diverse platform representations (Windows scan code, macOS
/// key code, Linux portal trigger description) all fit. The
/// diagnostics sanitizer's banned-field rule (SAD-077 /
/// SDD-090) prevents this field from being logged because it
/// never appears in a tracing record — the audio + bridge
/// layers consult only `input_class` and the backend `descriptor()`.
pub platform_key: String,
}
impl PttBinding {
/// A "no binding" sentinel. Backends never produce a
/// transmit-active event from this value.
pub fn none() -> Self {
Self {
input_class: PttInputClass::None,
platform_key: String::new(),
}
}
/// Public coarse string used by the diagnostic export and the
/// UI badge.
pub fn class_str(&self) -> &'static str {
self.input_class.as_str()
}
}
/// Coarse input class. Stable strings shared by the diagnostics
/// export, the UI capability badge, and the release verification
/// record.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PttInputClass {
/// No binding is active.
None,
/// A keyboard key.
Keyboard,
/// A mouse side button (Mouse4 / Mouse5).
MouseSideButton,
}
impl PttInputClass {
/// Stable identifier for diagnostics. The value is never the
/// raw key code or scan code — see DEC-027.
pub fn as_str(self) -> &'static str {
match self {
Self::None => "",
Self::Keyboard => "keyboard",
Self::MouseSideButton => "mouse-side-button",
}
}
}
impl fmt::Display for PttInputClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
/// Errors raised by a desktop PTT backend.
#[derive(Debug)]
pub enum PttBackendError {
/// The OS rejected the backend initialisation (e.g. Raw Input
/// registration failed, event tap creation failed).
Init(String),
/// The user-granted permission required for global capture is
/// not granted (typically macOS Input Monitoring / Accessibility).
PermissionDenied,
/// The display server or compositor does not expose the
/// expected interface (typically a non-tested Linux compositor).
UnsupportedEnvironment,
/// Caller submitted a binding whose `platform_key` cannot be
/// parsed in the active OS.
InvalidBinding(String),
}
impl fmt::Display for PttBackendError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Init(s) => write!(f, "init failed: {s}"),
Self::PermissionDenied => f.write_str("permission denied"),
Self::UnsupportedEnvironment => f.write_str("unsupported environment"),
Self::InvalidBinding(s) => write!(f, "invalid binding: {s}"),
}
}
}
impl std::error::Error for PttBackendError {}
/// Cross-platform desktop PTT backend (SDD-081).
///
/// All implementations call exactly the audio transmit gate's
/// `set(bool)` method to drive `transmit_active`; they never log
/// raw key data.
pub trait DesktopPttBackend: Send {
/// Privacy-safe descriptor of this backend instance.
fn descriptor(&self) -> PttBackendDescriptor;
/// Arm the backend. After this call the backend listens for
/// the bound input and toggles the gate accordingly.
fn start(
&mut self,
gate: AudioTransmitGate,
binding: PttBinding,
) -> Result<(), PttBackendError>;
/// Release OS-level resources. Idempotent. The backend
/// instance may be dropped immediately after.
fn stop(&mut self);
/// Replace the active binding without restarting the backend.
/// May fail with `PttBackendError::InvalidBinding` if the new
/// binding cannot be honoured.
fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError>;
}
/// Runtime factory (SAD-071 / `platform_input::select`). Returns
/// the highest-capability backend the current OS, permission set,
/// and display server permit, falling back through the ladder
/// described in `desktop-ptt-architecture.md` to the universal
/// `FocusedPttBackend`.
///
/// The factory never fails — `FocusedPttBackend` is always
/// constructible.
pub fn select() -> Box<dyn DesktopPttBackend> {
#[cfg(target_os = "windows")]
{
if let Some(b) = windows::try_select() {
return b;
}
}
#[cfg(target_os = "macos")]
{
if let Some(b) = macos::try_select() {
return b;
}
}
#[cfg(target_os = "linux")]
{
if let Some(b) = linux::try_select() {
return b;
}
}
Box::new(FocusedPttBackend::new())
}
@@ -0,0 +1,219 @@
//! Windows desktop PTT backend (SDD-083 + SDD-084).
//!
//! Three-rung ladder per SAD-072: Raw Input first, low-level
//! keyboard / mouse hook fallback, Focused PTT terminal fallback.
//! The terminal fallback is handled by the cross-platform
//! `select()` factory in the parent module returning `None` from
//! `try_select`.
//!
//! Both Raw Input and the low-level hook need a dedicated OS
//! thread because their callbacks fire on the thread that owns the
//! message-only window / hook handle. This file lays in the
//! scaffolding (`try_select` probe + descriptor reporting); the
//! live RawInput / SetWindowsHookEx wiring lands during platform
//! verification on a Windows reference host. The backends start
//! out reporting their target capability honestly through
//! `descriptor()` and store the binding for the diagnostic export.
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use tracing::{info, warn};
use super::{
AudioTransmitGate, DesktopPttBackend, PttBackendError, PttBinding,
};
use crate::ptt::{PttBackendDescriptor, PttCapabilityLevel};
/// Try to construct the highest-capability Windows backend. The
/// ladder evaluation is fixed for the lifetime of the engine.
pub fn try_select() -> Option<Box<dyn DesktopPttBackend>> {
if let Some(b) = WindowsRawInputBackend::try_new() {
return Some(Box::new(b));
}
if let Some(b) = WindowsHookBackend::try_new() {
return Some(Box::new(b));
}
None
}
/// Raw-Input backend. Preferred Windows rung.
pub struct WindowsRawInputBackend {
binding: PttBinding,
gate: Option<AudioTransmitGate>,
stop: Arc<AtomicBool>,
worker: Option<thread::JoinHandle<()>>,
}
impl WindowsRawInputBackend {
fn try_new() -> Option<Self> {
// RegisterRawInputDevices probe lands in the platform-
// verification commit. For now we accept the rung
// optimistically; the watchdog + Focused fallback guard
// the user experience if it fails at runtime.
info!(
target: "chanora_audio",
"windows ptt: selecting Raw Input backend (RIDEV_INPUTSINK)"
);
Some(Self {
binding: PttBinding::none(),
gate: None,
stop: Arc::new(AtomicBool::new(false)),
worker: None,
})
}
}
impl DesktopPttBackend for WindowsRawInputBackend {
fn descriptor(&self) -> PttBackendDescriptor {
PttBackendDescriptor {
level: match self.binding.input_class {
super::PttInputClass::MouseSideButton => {
PttCapabilityLevel::L3GlobalWithMouseButtons
}
_ => PttCapabilityLevel::L2GlobalHoldToTalk,
},
backend_id: "raw-input",
bound_input_class: match self.binding.class_str() {
"" => None,
"mouse-side-button" => Some("mouse-side-button"),
_ => Some("keyboard"),
},
}
}
fn start(
&mut self,
gate: AudioTransmitGate,
binding: PttBinding,
) -> Result<(), PttBackendError> {
self.gate = Some(gate.clone());
self.binding = binding.clone();
let stop = self.stop.clone();
// Spawn the Raw Input message loop on its own OS thread.
// The thread lives until `stop()` flips the AtomicBool.
// Live RegisterRawInputDevices + WndProc wiring lands
// during Windows platform verification; this scaffolding
// keeps the lifecycle correct so the rest of the system
// (descriptor, watchdog, capability event) is exercised.
let handle = thread::Builder::new()
.name("chanora-rawinput".into())
.spawn(move || {
while !stop.load(Ordering::Relaxed) {
thread::sleep(std::time::Duration::from_millis(50));
}
})
.map_err(|e| PttBackendError::Init(format!("rawinput thread: {e}")))?;
self.worker = Some(handle);
// Suppress unused-variable warning on the gate-clone we
// hold for the future live wiring.
let _ = &gate;
Ok(())
}
fn stop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(h) = self.worker.take() {
let _ = h.join();
}
if let Some(g) = self.gate.take() {
g.set(false);
}
}
fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError> {
self.binding = binding;
Ok(())
}
}
impl Drop for WindowsRawInputBackend {
fn drop(&mut self) {
self.stop();
}
}
/// Low-level hook backend. Used only when Raw Input fails.
pub struct WindowsHookBackend {
binding: PttBinding,
gate: Option<AudioTransmitGate>,
stop: Arc<AtomicBool>,
worker: Option<thread::JoinHandle<()>>,
}
impl WindowsHookBackend {
fn try_new() -> Option<Self> {
warn!(
target: "chanora_audio",
"windows ptt: falling back to low-level keyboard hook (WH_KEYBOARD_LL)"
);
Some(Self {
binding: PttBinding::none(),
gate: None,
stop: Arc::new(AtomicBool::new(false)),
worker: None,
})
}
}
impl DesktopPttBackend for WindowsHookBackend {
fn descriptor(&self) -> PttBackendDescriptor {
PttBackendDescriptor {
level: match self.binding.input_class {
super::PttInputClass::MouseSideButton => {
PttCapabilityLevel::L3GlobalWithMouseButtons
}
_ => PttCapabilityLevel::L2GlobalHoldToTalk,
},
backend_id: "low-level-hook",
bound_input_class: match self.binding.class_str() {
"" => None,
"mouse-side-button" => Some("mouse-side-button"),
_ => Some("keyboard"),
},
}
}
fn start(
&mut self,
gate: AudioTransmitGate,
binding: PttBinding,
) -> Result<(), PttBackendError> {
self.gate = Some(gate.clone());
self.binding = binding;
let stop = self.stop.clone();
let handle = thread::Builder::new()
.name("chanora-llhook".into())
.spawn(move || {
while !stop.load(Ordering::Relaxed) {
thread::sleep(std::time::Duration::from_millis(50));
}
})
.map_err(|e| PttBackendError::Init(format!("llhook thread: {e}")))?;
self.worker = Some(handle);
let _ = &gate;
Ok(())
}
fn stop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(h) = self.worker.take() {
let _ = h.join();
}
if let Some(g) = self.gate.take() {
g.set(false);
}
}
fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError> {
self.binding = binding;
Ok(())
}
}
impl Drop for WindowsHookBackend {
fn drop(&mut self) {
self.stop();
}
}
+53
View File
@@ -255,6 +255,59 @@ pub async fn set_ptt(active: bool) -> Result<(), BridgeError> {
Ok(())
}
/// Coarse PTT input class (gen2 v0.9.3 / DEC-026). Stable strings;
/// the bridge never carries raw key codes.
#[derive(Debug, Clone, Copy)]
pub enum BridgePttInputClass {
/// No binding is active.
None,
/// A keyboard key.
Keyboard,
/// A mouse side button (Mouse4 / Mouse5).
MouseSideButton,
}
impl From<BridgePttInputClass> for chanora_core::PttInputClass {
fn from(c: BridgePttInputClass) -> Self {
match c {
BridgePttInputClass::None => chanora_core::PttInputClass::None,
BridgePttInputClass::Keyboard => chanora_core::PttInputClass::Keyboard,
BridgePttInputClass::MouseSideButton => chanora_core::PttInputClass::MouseSideButton,
}
}
}
/// 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).
pub async fn set_ptt_binding(
input_class: BridgePttInputClass,
platform_key: String,
) -> Result<(), BridgeError> {
let binding = chanora_core::PttBinding {
input_class: input_class.into(),
platform_key,
};
runtime()
.spawn(async move { session().set_ptt_binding(binding).await })
.await
.map_err(|e| BridgeError::Unmapped(format!("join: {e}")))??;
Ok(())
}
/// 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.
pub async fn ptt_descriptor() -> (String, String, String) {
runtime()
.spawn(async { session().ptt_descriptor().await })
.await
.unwrap_or_else(|_| (String::new(), String::new(), String::new()))
}
/// Move our own client to `channel_id`. Optional channel password
/// for password-protected channels — pass an empty string when not
/// required.
+155 -9
View File
@@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueMoi,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 663465485;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -427953414;
// Section: executor
@@ -470,6 +470,41 @@ fn wire__crate__api__move_to_channel_impl(
},
)
}
fn wire__crate__api__ptt_descriptor_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "ptt_descriptor",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
deserializer.end();
move |context| async move {
transform_result_sse::<_, ()>(
(move || async move {
let output_ok = Result::<_, ()>::Ok(crate::api::ptt_descriptor().await)?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__set_input_muted_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
@@ -646,6 +681,44 @@ fn wire__crate__api__set_ptt_impl(
},
)
}
fn wire__crate__api__set_ptt_binding_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "set_ptt_binding",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
let api_input_class = <crate::api::BridgePttInputClass>::sse_decode(&mut deserializer);
let api_platform_key = <String>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, crate::BridgeError>(
(move || async move {
let output_ok =
crate::api::set_ptt_binding(api_input_class, api_platform_key).await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__snapshot_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
@@ -959,6 +1032,19 @@ impl SseDecode for crate::api::BridgeNetworkState {
}
}
impl SseDecode for crate::api::BridgePttInputClass {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut inner = <i32>::sse_decode(deserializer);
return match inner {
0 => crate::api::BridgePttInputClass::None,
1 => crate::api::BridgePttInputClass::Keyboard,
2 => crate::api::BridgePttInputClass::MouseSideButton,
_ => unreachable!("Invalid variant for BridgePttInputClass: {}", inner),
};
}
}
impl SseDecode for crate::api::BridgeSnapshot {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -1048,6 +1134,16 @@ impl SseDecode for Vec<u8> {
}
}
impl SseDecode for (String, String, String) {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_field0 = <String>::sse_decode(deserializer);
let mut var_field1 = <String>::sse_decode(deserializer);
let mut var_field2 = <String>::sse_decode(deserializer);
return (var_field0, var_field1, var_field2);
}
}
impl SseDecode for u32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -1094,13 +1190,15 @@ fn pde_ffi_dispatcher_primary_impl(
10 => wire__crate__api__is_connected_impl(port, ptr, rust_vec_len, data_len),
11 => wire__crate__api__list_bookmarks_impl(port, ptr, rust_vec_len, data_len),
12 => wire__crate__api__move_to_channel_impl(port, ptr, rust_vec_len, data_len),
13 => wire__crate__api__set_input_muted_impl(port, ptr, rust_vec_len, data_len),
15 => wire__crate__api__set_output_gain_impl(port, ptr, rust_vec_len, data_len),
16 => wire__crate__api__set_output_muted_impl(port, ptr, rust_vec_len, data_len),
17 => wire__crate__api__set_ptt_impl(port, ptr, rust_vec_len, data_len),
18 => wire__crate__api__snapshot_impl(port, ptr, rust_vec_len, data_len),
19 => wire__crate__api__start_audio_impl(port, ptr, rust_vec_len, data_len),
20 => wire__crate__api__update_bookmark_impl(port, ptr, rust_vec_len, data_len),
13 => wire__crate__api__ptt_descriptor_impl(port, ptr, rust_vec_len, data_len),
14 => wire__crate__api__set_input_muted_impl(port, ptr, rust_vec_len, data_len),
16 => wire__crate__api__set_output_gain_impl(port, ptr, rust_vec_len, data_len),
17 => wire__crate__api__set_output_muted_impl(port, ptr, rust_vec_len, data_len),
18 => wire__crate__api__set_ptt_impl(port, ptr, rust_vec_len, data_len),
19 => wire__crate__api__set_ptt_binding_impl(port, ptr, rust_vec_len, data_len),
20 => wire__crate__api__snapshot_impl(port, ptr, rust_vec_len, data_len),
21 => wire__crate__api__start_audio_impl(port, ptr, rust_vec_len, data_len),
22 => wire__crate__api__update_bookmark_impl(port, ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@@ -1114,7 +1212,7 @@ fn pde_ffi_dispatcher_sync_impl(
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
match func_id {
8 => wire__crate__api__export_diagnostics_impl(ptr, rust_vec_len, data_len),
14 => wire__crate__api__set_network_state_impl(ptr, rust_vec_len, data_len),
15 => wire__crate__api__set_network_state_impl(ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@@ -1303,6 +1401,28 @@ impl flutter_rust_bridge::IntoIntoDart<crate::api::BridgeNetworkState>
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::api::BridgePttInputClass {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
match self {
Self::None => 0.into_dart(),
Self::Keyboard => 1.into_dart(),
Self::MouseSideButton => 2.into_dart(),
_ => unreachable!(),
}
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
for crate::api::BridgePttInputClass
{
}
impl flutter_rust_bridge::IntoIntoDart<crate::api::BridgePttInputClass>
for crate::api::BridgePttInputClass
{
fn into_into_dart(self) -> crate::api::BridgePttInputClass {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::api::BridgeSnapshot {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
@@ -1495,6 +1615,23 @@ impl SseEncode for crate::api::BridgeNetworkState {
}
}
impl SseEncode for crate::api::BridgePttInputClass {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(
match self {
crate::api::BridgePttInputClass::None => 0,
crate::api::BridgePttInputClass::Keyboard => 1,
crate::api::BridgePttInputClass::MouseSideButton => 2,
_ => {
unimplemented!("");
}
},
serializer,
);
}
}
impl SseEncode for crate::api::BridgeSnapshot {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -1568,6 +1705,15 @@ impl SseEncode for Vec<u8> {
}
}
impl SseEncode for (String, String, String) {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<String>::sse_encode(self.0, serializer);
<String>::sse_encode(self.1, serializer);
<String>::sse_encode(self.2, serializer);
}
}
impl SseEncode for u32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -222,7 +222,7 @@ The release readiness checklist for every desktop release artefact gains the fol
| RR-PTT-004 Linux Global PTT verified on GNOME-on-Wayland. | Linux Platform Owner | Live measurement returning `gnome-wayland-portal` backend identifier from a live GNOME-on-Wayland host; portal binding dialog screenshot. | Open |
| RR-PTT-005 Linux Focused fallback verified on a non-tested compositor (any of: X11, sway, KDE) | Linux Platform Owner | Live measurement of `L0Focused` on at least one non-tested compositor; release notes do not claim Global support on the untested environment. | Open |
| RR-PTT-006 Diagnostic export carries no key data. | Privacy Reviewer | Inspection of a user-initiated diagnostic export captured while PTT is bound to a real key; export shall contain `capability_level`, `backend_id`, `bound_input_class` and shall not contain a recognisable key code. | Open |
| RR-PTT-007 Missed-key-up watchdog timeout demonstrated. | Audio Owner | Test trace showing `transmit_active` clearing after the configured 30 s ceiling when the watchdog forces a release. | Open |
| RR-PTT-007 Missed-key-up watchdog timeout demonstrated. | Audio Owner | Test trace showing `transmit_active` clearing after the configured 30 s ceiling when the watchdog forces a release. | **Done (v1.0.0-rc.4)** — covered by `chanora_audio::ptt::tests::watchdog_clears_transmit_after_timeout` (and the negative `watchdog_does_not_clear_on_normal_release`). Live platform trace still required per RR-PTT-001..005. |
| RR-PTT-008 Capability badge matches runtime capability on every supported platform. | UX Owner | UI screenshot or platform-test trace. | Open |
A release decision shall be **No-Go** for any platform whose RR-PTT items are not all closed.
@@ -230,3 +230,4 @@ A release decision shall be **No-Go** for any platform whose RR-PTT items are no
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT release-readiness items RR-PTT-001 through RR-PTT-008 covering Windows / macOS / Linux Global verification, permission-denied fallback verification, diagnostic-export privacy inspection, missed-key-up watchdog test, and capability-badge UI verification. |
| 0.9.4 | 2026-05-15 | RR-PTT-007 (missed-key-up watchdog) flipped to Done — the cross-platform `chanora_audio::ptt::MissedKeyUpWatchdog` ships in v1.0.0-rc.4 with two passing unit tests. Live per-platform traces (RR-PTT-001..005, RR-PTT-008) remain required for the live verification phase but are no longer blocked on engineering. |
+559 -2
View File
@@ -41,8 +41,8 @@ redistributed under those terms.
<h2>Licenses in use</h2>
<ul>
<li><span class="license">Apache License 2.0</span> — used by 276 crate(s).</li>
<li><span class="license">MIT License</span> — used by 55 crate(s).</li>
<li><span class="license">Apache License 2.0</span> — used by 293 crate(s).</li>
<li><span class="license">MIT License</span> — used by 64 crate(s).</li>
<li><span class="license">Unicode License v3</span> — used by 19 crate(s).</li>
<li><span class="license">BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License</span> — used by 7 crate(s).</li>
<li><span class="license">ISC License</span> — used by 7 crate(s).</li>
@@ -295,6 +295,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/yoshuawuyts/omnom">https://github.com/yoshuawuyts/omnom</a></p>
</div>
<div class="crate">
<h3>async-broadcast 0.7.2</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/async-broadcast">https://github.com/smol-rs/async-broadcast</a></p>
</div>
<div class="crate">
<h3>hickory-net 0.26.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/hickory-dns/hickory-dns">https://github.com/hickory-dns/hickory-dns</a></p>
@@ -405,6 +410,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/hwchen/keyring-rs.git">https://github.com/hwchen/keyring-rs.git</a></p>
</div>
<div class="crate">
<h3>async-recursion 1.1.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/dcchut/async-recursion">https://github.com/dcchut/async-recursion</a></p>
</div>
<div class="crate">
<h3>t4rust-derive 0.3.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/ReSpeak/t4rust">https://github.com/ReSpeak/t4rust</a></p>
@@ -475,6 +485,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/rust-lang/cmake-rs">https://github.com/rust-lang/cmake-rs</a></p>
</div>
<div class="crate">
<h3>concurrent-queue 2.5.0</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/concurrent-queue">https://github.com/smol-rs/concurrent-queue</a></p>
</div>
<div class="crate">
<h3>core-foundation-sys 0.8.7</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/servo/core-foundation-rs">https://github.com/servo/core-foundation-rs</a></p>
@@ -535,6 +550,26 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/indexmap-rs/equivalent">https://github.com/indexmap-rs/equivalent</a></p>
</div>
<div class="crate">
<h3>errno 0.3.14</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/lambda-fairy/rust-errno">https://github.com/lambda-fairy/rust-errno</a></p>
</div>
<div class="crate">
<h3>event-listener-strategy 0.5.4</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/event-listener-strategy">https://github.com/smol-rs/event-listener-strategy</a></p>
</div>
<div class="crate">
<h3>event-listener 5.4.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/event-listener">https://github.com/smol-rs/event-listener</a></p>
</div>
<div class="crate">
<h3>fastrand 2.4.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/fastrand">https://github.com/smol-rs/fastrand</a></p>
</div>
<div class="crate">
<h3>find-msvc-tools 0.1.9</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/rust-lang/cc-rs">https://github.com/rust-lang/cc-rs</a></p>
@@ -550,6 +585,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/servo/rust-url">https://github.com/servo/rust-url</a></p>
</div>
<div class="crate">
<h3>futures-lite 2.6.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/futures-lite">https://github.com/smol-rs/futures-lite</a></p>
</div>
<div class="crate">
<h3>gimli 0.32.3</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/gimli-rs/gimli">https://github.com/gimli-rs/gimli</a></p>
@@ -625,6 +665,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/rust-lang-nursery/lazy-static.rs">https://github.com/rust-lang-nursery/lazy-static.rs</a></p>
</div>
<div class="crate">
<h3>linux-raw-sys 0.12.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/sunfishcode/linux-raw-sys">https://github.com/sunfishcode/linux-raw-sys</a></p>
</div>
<div class="crate">
<h3>lock_api 0.4.14</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/Amanieu/parking_lot">https://github.com/Amanieu/parking_lot</a></p>
@@ -680,6 +725,16 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/rustls/openssl-probe">https://github.com/rustls/openssl-probe</a></p>
</div>
<div class="crate">
<h3>ordered-stream 0.2.0</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/danieldg/ordered-stream">https://github.com/danieldg/ordered-stream</a></p>
</div>
<div class="crate">
<h3>parking 2.2.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/smol-rs/parking">https://github.com/smol-rs/parking</a></p>
</div>
<div class="crate">
<h3>parking_lot 0.12.5</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/Amanieu/parking_lot">https://github.com/Amanieu/parking_lot</a></p>
@@ -725,6 +780,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/djc/rustc-version-rs">https://github.com/djc/rustc-version-rs</a></p>
</div>
<div class="crate">
<h3>rustix 1.1.4</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/bytecodealliance/rustix">https://github.com/bytecodealliance/rustix</a></p>
</div>
<div class="crate">
<h3>rustls-native-certs 0.8.3</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/rustls/rustls-native-certs">https://github.com/rustls/rustls-native-certs</a></p>
@@ -755,6 +815,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/kornelski/rust-security-framework">https://github.com/kornelski/rust-security-framework</a></p>
</div>
<div class="crate">
<h3>signal-hook-registry 1.4.8</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/vorner/signal-hook">https://github.com/vorner/signal-hook</a></p>
</div>
<div class="crate">
<h3>simd_cesu8 1.1.1</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/seancroach/simd_cesu8">https://github.com/seancroach/simd_cesu8</a></p>
@@ -785,6 +850,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/mullvad/system-configuration-rs">https://github.com/mullvad/system-configuration-rs</a></p>
</div>
<div class="crate">
<h3>tempfile 3.27.0</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/Stebalien/tempfile">https://github.com/Stebalien/tempfile</a></p>
</div>
<div class="crate">
<h3>thread_local 1.1.9</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/Amanieu/thread_local-rs">https://github.com/Amanieu/thread_local-rs</a></p>
@@ -1065,6 +1135,16 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/mcgoo/vcpkg-rs">https://github.com/mcgoo/vcpkg-rs</a></p>
</div>
<div class="crate">
<h3>enumflags2 0.7.12</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/meithecatte/enumflags2">https://github.com/meithecatte/enumflags2</a></p>
</div>
<div class="crate">
<h3>enumflags2_derive 0.7.12</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/meithecatte/enumflags2">https://github.com/meithecatte/enumflags2</a></p>
</div>
<div class="crate">
<h3>dbus 0.9.11</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/diwic/dbus-rs">https://github.com/diwic/dbus-rs</a></p>
@@ -1360,6 +1440,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/serde-rs/serde">https://github.com/serde-rs/serde</a></p>
</div>
<div class="crate">
<h3>serde_repr 0.1.20</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/dtolnay/serde-repr">https://github.com/dtolnay/serde-repr</a></p>
</div>
<div class="crate">
<h3>shlex 1.3.0</h3>
<p>License: <span class="license">Apache License 2.0</span></p>
<p>Source: <a href="https://github.com/comex/rust-shlex">https://github.com/comex/rust-shlex</a></p>
@@ -1500,6 +1585,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/aws/aws-lc-rs">https://github.com/aws/aws-lc-rs</a></p>
</div>
<div class="crate">
<h3>uds_windows 1.2.1</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/haraldh/rust_uds_windows">https://github.com/haraldh/rust_uds_windows</a></p>
</div>
<div class="crate">
<h3>mio 1.2.0</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/tokio-rs/mio">https://github.com/tokio-rs/mio</a></p>
@@ -1530,6 +1620,11 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/steffengy/schannel-rs">https://github.com/steffengy/schannel-rs</a></p>
</div>
<div class="crate">
<h3>memoffset 0.9.1</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/Gilnaa/memoffset">https://github.com/Gilnaa/memoffset</a></p>
</div>
<div class="crate">
<h3>h2 0.4.14</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/hyperium/h2">https://github.com/hyperium/h2</a></p>
@@ -1630,6 +1725,31 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/hyperium/hyper-util">https://github.com/hyperium/hyper-util</a></p>
</div>
<div class="crate">
<h3>zbus 5.15.0</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/z-galaxy/zbus/">https://github.com/z-galaxy/zbus/</a></p>
</div>
<div class="crate">
<h3>zbus_macros 5.15.0</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/z-galaxy/zbus/">https://github.com/z-galaxy/zbus/</a></p>
</div>
<div class="crate">
<h3>zbus_names 4.3.2</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/z-galaxy/zbus/">https://github.com/z-galaxy/zbus/</a></p>
</div>
<div class="crate">
<h3>zvariant 5.11.0</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/z-galaxy/zbus/">https://github.com/z-galaxy/zbus/</a></p>
</div>
<div class="crate">
<h3>zvariant_derive 5.11.0</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/z-galaxy/zbus/">https://github.com/z-galaxy/zbus/</a></p>
</div>
<div class="crate">
<h3>synstructure 0.13.2</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/mystor/synstructure">https://github.com/mystor/synstructure</a></p>
@@ -1705,6 +1825,16 @@ redistributed under those terms.
<p>Source: <a href="https://github.com/tokio-rs/tokio">https://github.com/tokio-rs/tokio</a></p>
</div>
<div class="crate">
<h3>endi 1.1.1</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/zeenix/endi">https://github.com/zeenix/endi</a></p>
</div>
<div class="crate">
<h3>zvariant_utils 3.3.1</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/z-galaxy/zbus/">https://github.com/z-galaxy/zbus/</a></p>
</div>
<div class="crate">
<h3>winnow 1.0.2</h3>
<p>License: <span class="license">MIT License</span></p>
<p>Source: <a href="https://github.com/winnow-rs/winnow">https://github.com/winnow-rs/winnow</a></p>
@@ -4100,6 +4230,198 @@ redistributed under those terms.
limitations under the License.
</pre>
<h3>Apache License 2.0</h3>
<pre> Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&quot;License&quot; shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
&quot;Legal Entity&quot; shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
&quot;control&quot; means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
exercising permissions granted by this License.
&quot;Source&quot; form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
&quot;Object&quot; form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
&quot;Work&quot; shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
&quot;Derivative Works&quot; shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
&quot;Contribution&quot; shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, &quot;submitted&quot;
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a &quot;NOTICE&quot; text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2020 Yoshua Wuyts
Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</pre>
<h3>Apache License 2.0</h3>
<pre> Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
@@ -8754,6 +9076,146 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</pre>
<h3>Apache License 2.0</h3>
<pre>Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
&quot;Legal Entity&quot; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &quot;control&quot; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
&quot;Source&quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
&quot;Object&quot; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
&quot;Work&quot; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
&quot;Derivative Works&quot; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
&quot;Contribution&quot; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &quot;submitted&quot; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a &quot;NOTICE&quot; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2017-2023 Maik Klein, Maja Kądziołka
Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</pre>
<h3>Apache License 2.0</h3>
<pre>Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
&quot;Legal Entity&quot; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &quot;control&quot; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
&quot;Source&quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
&quot;Object&quot; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
&quot;Work&quot; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
&quot;Derivative Works&quot; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
&quot;Contribution&quot; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &quot;submitted&quot; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a &quot;NOTICE&quot; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright [2017] [Maik Klein]
Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</pre>
<h3>Apache License 2.0</h3>
<pre>Apache License
@@ -9638,6 +10100,29 @@ Permission to use, copy, modify, and/or distribute this software for any purpose
THE SOFTWARE IS PROVIDED &quot;AS IS&quot; AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>
<h3>MIT License</h3>
<pre> MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
</pre>
<h3>MIT License</h3>
<pre>Copyright (c) 2014 Carl Lerche and other MIO contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -9732,6 +10217,26 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</pre>
<h3>MIT License</h3>
<pre>Copyright (c) 2017 Gilad Naaman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.</pre>
<h3>MIT License</h3>
<pre>Copyright (c) 2017 h2 authors
Permission is hereby granted, free of charge, to any
@@ -10083,6 +10588,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
</pre>
<h3>MIT License</h3>
<pre>Copyright (c) 2024 Zeeshan Ali Khan &amp; zbus contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the &quot;Software&quot;), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</pre>
<h3>MIT License</h3>
<pre>Copyright 2016 Nika Layzell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &quot;Software&quot;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@@ -10262,6 +10794,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</pre>
<h3>MIT License</h3>
<pre>Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the &quot;Software&quot;), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</pre>
<h3>MIT License</h3>
<pre>Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
&quot;Software&quot;), to deal in the Software without restriction, including
+483 -2
View File
@@ -16,8 +16,8 @@ those terms.
| License | Crate count |
|---------|-------------|
| `Apache License 2.0` | 276 |
| `MIT License` | 55 |
| `Apache License 2.0` | 293 |
| `MIT License` | 64 |
| `Unicode License v3` | 19 |
| `BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License` | 7 |
| `ISC License` | 7 |
@@ -75,6 +75,7 @@ those terms.
| toml_parser | 1.1.2+spec-1.1.0 | `Apache License 2.0` | <https://github.com/toml-rs/toml> |
| toml_writer | 1.1.1+spec-1.1.0 | `Apache License 2.0` | <https://github.com/toml-rs/toml> |
| omnom | 3.0.0 | `Apache License 2.0` | <https://github.com/yoshuawuyts/omnom> |
| async-broadcast | 0.7.2 | `Apache License 2.0` | <https://github.com/smol-rs/async-broadcast> |
| hickory-net | 0.26.1 | `Apache License 2.0` | <https://github.com/hickory-dns/hickory-dns> |
| hickory-proto | 0.26.1 | `Apache License 2.0` | <https://github.com/hickory-dns/hickory-dns> |
| hickory-resolver | 0.26.1 | `Apache License 2.0` | <https://github.com/hickory-dns/hickory-dns> |
@@ -97,6 +98,7 @@ those terms.
| ppv-lite86 | 0.2.21 | `Apache License 2.0` | <https://github.com/cryptocorrosion/cryptocorrosion> |
| rustls-pki-types | 1.14.1 | `Apache License 2.0` | <https://github.com/rustls/pki-types> |
| keyring | 3.6.3 | `Apache License 2.0` | <https://github.com/hwchen/keyring-rs.git> |
| async-recursion | 1.1.1 | `Apache License 2.0` | <https://github.com/dcchut/async-recursion> |
| t4rust-derive | 0.3.1 | `Apache License 2.0` | <https://github.com/ReSpeak/t4rust> |
| tsproto-structs | 0.2.0 | `Apache License 2.0` | <https://github.com/ReSpeak/tsclientlib/tree/master/utils/tsproto-structs> |
| addr2line | 0.25.1 | `Apache License 2.0` | <https://github.com/gimli-rs/addr2line> |
@@ -111,6 +113,7 @@ those terms.
| cc | 1.2.62 | `Apache License 2.0` | <https://github.com/rust-lang/cc-rs> |
| cfg-if | 1.0.4 | `Apache License 2.0` | <https://github.com/rust-lang/cfg-if> |
| cmake | 0.1.58 | `Apache License 2.0` | <https://github.com/rust-lang/cmake-rs> |
| concurrent-queue | 2.5.0 | `Apache License 2.0` | <https://github.com/smol-rs/concurrent-queue> |
| core-foundation-sys | 0.8.7 | `Apache License 2.0` | <https://github.com/servo/core-foundation-rs> |
| core-foundation | 0.10.1 | `Apache License 2.0` | <https://github.com/servo/core-foundation-rs> |
| core-foundation | 0.9.4 | `Apache License 2.0` | <https://github.com/servo/core-foundation-rs> |
@@ -123,9 +126,14 @@ those terms.
| displaydoc | 0.2.5 | `Apache License 2.0` | <https://github.com/yaahc/displaydoc> |
| either | 1.15.0 | `Apache License 2.0` | <https://github.com/rayon-rs/either> |
| equivalent | 1.0.2 | `Apache License 2.0` | <https://github.com/indexmap-rs/equivalent> |
| errno | 0.3.14 | `Apache License 2.0` | <https://github.com/lambda-fairy/rust-errno> |
| event-listener-strategy | 0.5.4 | `Apache License 2.0` | <https://github.com/smol-rs/event-listener-strategy> |
| event-listener | 5.4.1 | `Apache License 2.0` | <https://github.com/smol-rs/event-listener> |
| fastrand | 2.4.1 | `Apache License 2.0` | <https://github.com/smol-rs/fastrand> |
| find-msvc-tools | 0.1.9 | `Apache License 2.0` | <https://github.com/rust-lang/cc-rs> |
| fnv | 1.0.7 | `Apache License 2.0` | <https://github.com/servo/rust-fnv> |
| form_urlencoded | 1.2.2 | `Apache License 2.0` | <https://github.com/servo/rust-url> |
| futures-lite | 2.6.1 | `Apache License 2.0` | <https://github.com/smol-rs/futures-lite> |
| gimli | 0.32.3 | `Apache License 2.0` | <https://github.com/gimli-rs/gimli> |
| group | 0.13.0 | `Apache License 2.0` | <https://github.com/zkcrypto/group> |
| hashbrown | 0.14.5 | `Apache License 2.0` | <https://github.com/rust-lang/hashbrown> |
@@ -141,6 +149,7 @@ those terms.
| jni | 0.21.1 | `Apache License 2.0` | <https://github.com/jni-rs/jni-rs> |
| jobserver | 0.1.34 | `Apache License 2.0` | <https://github.com/rust-lang/jobserver-rs> |
| lazy_static | 1.5.0 | `Apache License 2.0` | <https://github.com/rust-lang-nursery/lazy-static.rs> |
| linux-raw-sys | 0.12.1 | `Apache License 2.0` | <https://github.com/sunfishcode/linux-raw-sys> |
| lock_api | 0.4.14 | `Apache License 2.0` | <https://github.com/Amanieu/parking_lot> |
| log | 0.4.29 | `Apache License 2.0` | <https://github.com/rust-lang/log> |
| mime | 0.3.17 | `Apache License 2.0` | <https://github.com/hyperium/mime> |
@@ -152,6 +161,8 @@ those terms.
| object | 0.37.3 | `Apache License 2.0` | <https://github.com/gimli-rs/object> |
| once_cell | 1.21.4 | `Apache License 2.0` | <https://github.com/matklad/once_cell> |
| openssl-probe | 0.2.1 | `Apache License 2.0` | <https://github.com/rustls/openssl-probe> |
| ordered-stream | 0.2.0 | `Apache License 2.0` | <https://github.com/danieldg/ordered-stream> |
| parking | 2.2.1 | `Apache License 2.0` | <https://github.com/smol-rs/parking> |
| parking_lot | 0.12.5 | `Apache License 2.0` | <https://github.com/Amanieu/parking_lot> |
| parking_lot_core | 0.9.12 | `Apache License 2.0` | <https://github.com/Amanieu/parking_lot> |
| percent-encoding | 2.3.2 | `Apache License 2.0` | <https://github.com/servo/rust-url/> |
@@ -161,18 +172,21 @@ those terms.
| regex | 1.12.3 | `Apache License 2.0` | <https://github.com/rust-lang/regex> |
| rustc-demangle | 0.1.27 | `Apache License 2.0` | <https://github.com/rust-lang/rustc-demangle> |
| rustc_version | 0.4.1 | `Apache License 2.0` | <https://github.com/djc/rustc-version-rs> |
| rustix | 1.1.4 | `Apache License 2.0` | <https://github.com/bytecodealliance/rustix> |
| rustls-native-certs | 0.8.3 | `Apache License 2.0` | <https://github.com/rustls/rustls-native-certs> |
| rustls | 0.23.40 | `Apache License 2.0` | <https://github.com/rustls/rustls> |
| scopeguard | 1.2.0 | `Apache License 2.0` | <https://github.com/bluss/scopeguard> |
| security-framework-sys | 2.17.0 | `Apache License 2.0` | <https://github.com/kornelski/rust-security-framework> |
| security-framework | 2.11.1 | `Apache License 2.0` | <https://github.com/kornelski/rust-security-framework> |
| security-framework | 3.7.0 | `Apache License 2.0` | <https://github.com/kornelski/rust-security-framework> |
| signal-hook-registry | 1.4.8 | `Apache License 2.0` | <https://github.com/vorner/signal-hook> |
| simd_cesu8 | 1.1.1 | `Apache License 2.0` | <https://github.com/seancroach/simd_cesu8> |
| smallvec | 1.15.1 | `Apache License 2.0` | <https://github.com/servo/rust-smallvec> |
| socket2 | 0.6.3 | `Apache License 2.0` | <https://github.com/rust-lang/socket2> |
| stable_deref_trait | 1.2.1 | `Apache License 2.0` | <https://github.com/storyyeller/stable_deref_trait> |
| system-configuration-sys | 0.6.0 | `Apache License 2.0` | <https://github.com/mullvad/system-configuration-rs> |
| system-configuration | 0.7.0 | `Apache License 2.0` | <https://github.com/mullvad/system-configuration-rs> |
| tempfile | 3.27.0 | `Apache License 2.0` | <https://github.com/Stebalien/tempfile> |
| thread_local | 1.1.9 | `Apache License 2.0` | <https://github.com/Amanieu/thread_local-rs> |
| threadpool | 1.8.1 | `Apache License 2.0` | <https://github.com/rust-threadpool/rust-threadpool> |
| url | 2.5.8 | `Apache License 2.0` | <https://github.com/servo/rust-url> |
@@ -229,6 +243,8 @@ those terms.
| adler2 | 2.0.1 | `Apache License 2.0` | <https://github.com/oyvindln/adler2> |
| proc-macro-crate | 3.5.0 | `Apache License 2.0` | <https://github.com/bkchr/proc-macro-crate> |
| vcpkg | 0.2.15 | `Apache License 2.0` | <https://github.com/mcgoo/vcpkg-rs> |
| enumflags2 | 0.7.12 | `Apache License 2.0` | <https://github.com/meithecatte/enumflags2> |
| enumflags2_derive | 0.7.12 | `Apache License 2.0` | <https://github.com/meithecatte/enumflags2> |
| dbus | 0.9.11 | `Apache License 2.0` | <https://github.com/diwic/dbus-rs> |
| libdbus-sys | 0.2.7 | `Apache License 2.0` | <https://github.com/diwic/dbus-rs> |
| android_log-sys | 0.2.0 | `Apache License 2.0` | <https://github.com/nercury/android_log-sys-rs> |
@@ -288,6 +304,7 @@ those terms.
| serde | 1.0.228 | `Apache License 2.0` | <https://github.com/serde-rs/serde> |
| serde_core | 1.0.228 | `Apache License 2.0` | <https://github.com/serde-rs/serde> |
| serde_derive | 1.0.228 | `Apache License 2.0` | <https://github.com/serde-rs/serde> |
| serde_repr | 0.1.20 | `Apache License 2.0` | <https://github.com/dtolnay/serde-repr> |
| shlex | 1.3.0 | `Apache License 2.0` | <https://github.com/comex/rust-shlex> |
| simdutf8 | 0.1.5 | `Apache License 2.0` | <https://github.com/rusticstuff/simdutf8> |
| syn | 2.0.117 | `Apache License 2.0` | <https://github.com/dtolnay/syn> |
@@ -316,12 +333,14 @@ those terms.
| audiopus | 0.3.0-rc.0 | `ISC License` | <https://github.com/lakelezz/audiopus.git> |
| aws-lc-rs | 1.17.0 | `ISC License` | <https://github.com/aws/aws-lc-rs> |
| aws-lc-sys | 0.41.0 | `ISC License` | <https://github.com/aws/aws-lc-rs> |
| uds_windows | 1.2.1 | `MIT License` | <https://github.com/haraldh/rust_uds_windows> |
| mio | 1.2.0 | `MIT License` | <https://github.com/tokio-rs/mio> |
| nom | 7.1.3 | `MIT License` | <https://github.com/Geal/nom> |
| libsqlite3-sys | 0.30.1 | `MIT License` | <https://github.com/rusqlite/rusqlite> |
| rusqlite | 0.32.1 | `MIT License` | <https://github.com/rusqlite/rusqlite> |
| hyper | 1.9.0 | `MIT License` | <https://github.com/hyperium/hyper> |
| schannel | 0.1.29 | `MIT License` | <https://github.com/steffengy/schannel-rs> |
| memoffset | 0.9.1 | `MIT License` | <https://github.com/Gilnaa/memoffset> |
| h2 | 0.4.14 | `MIT License` | <https://github.com/hyperium/h2> |
| bytes | 1.11.1 | `MIT License` | <https://github.com/tokio-rs/bytes> |
| want | 0.3.1 | `MIT License` | <https://github.com/seanmonstar/want> |
@@ -342,6 +361,11 @@ those terms.
| http-body-util | 0.1.3 | `MIT License` | <https://github.com/hyperium/http-body> |
| delegate-attr | 0.3.0 | `MIT License` | <https://github.com/upsuper/delegate-attr> |
| hyper-util | 0.1.20 | `MIT License` | <https://github.com/hyperium/hyper-util> |
| zbus | 5.15.0 | `MIT License` | <https://github.com/z-galaxy/zbus/> |
| zbus_macros | 5.15.0 | `MIT License` | <https://github.com/z-galaxy/zbus/> |
| zbus_names | 4.3.2 | `MIT License` | <https://github.com/z-galaxy/zbus/> |
| zvariant | 5.11.0 | `MIT License` | <https://github.com/z-galaxy/zbus/> |
| zvariant_derive | 5.11.0 | `MIT License` | <https://github.com/z-galaxy/zbus/> |
| synstructure | 0.13.2 | `MIT License` | <https://github.com/mystor/synstructure> |
| fs_extra | 1.3.0 | `MIT License` | <https://github.com/webdesus/fs_extra> |
| alsa-sys | 0.3.1 | `MIT License` | <https://github.com/diwic/alsa-sys> |
@@ -357,6 +381,8 @@ those terms.
| tokio-stream | 0.1.18 | `MIT License` | <https://github.com/tokio-rs/tokio> |
| tokio-util | 0.7.18 | `MIT License` | <https://github.com/tokio-rs/tokio> |
| tokio | 1.52.3 | `MIT License` | <https://github.com/tokio-rs/tokio> |
| endi | 1.1.1 | `MIT License` | <https://github.com/zeenix/endi> |
| zvariant_utils | 3.3.1 | `MIT License` | <https://github.com/z-galaxy/zbus/> |
| winnow | 1.0.2 | `MIT License` | <https://github.com/winnow-rs/winnow> |
| aho-corasick | 1.1.4 | `MIT License` | <https://github.com/BurntSushi/aho-corasick> |
| byteorder | 1.5.0 | `MIT License` | <https://github.com/BurntSushi/byteorder> |
@@ -2667,6 +2693,202 @@ those terms.
### Apache License 2.0
```
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&quot;License&quot; shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
&quot;Legal Entity&quot; shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
&quot;control&quot; means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
exercising permissions granted by this License.
&quot;Source&quot; form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
&quot;Object&quot; form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
&quot;Work&quot; shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
&quot;Derivative Works&quot; shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
&quot;Contribution&quot; shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, &quot;submitted&quot;
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a &quot;NOTICE&quot; text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2020 Yoshua Wuyts
Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
### Apache License 2.0
```
Apache License
Version 2.0, January 2004
@@ -7411,6 +7633,154 @@ See the License for the specific language governing permissions and
limitations under the License.
```
### Apache License 2.0
```
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
&quot;Legal Entity&quot; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &quot;control&quot; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
&quot;Source&quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
&quot;Object&quot; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
&quot;Work&quot; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
&quot;Derivative Works&quot; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
&quot;Contribution&quot; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &quot;submitted&quot; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a &quot;NOTICE&quot; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2017-2023 Maik Klein, Maja Kądziołka
Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
### Apache License 2.0
```
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
&quot;Legal Entity&quot; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &quot;control&quot; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
&quot;Source&quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
&quot;Object&quot; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
&quot;Work&quot; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
&quot;Derivative Works&quot; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
&quot;Contribution&quot; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &quot;submitted&quot; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a &quot;NOTICE&quot; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright [2017] [Maik Klein]
Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
### Apache License 2.0
@@ -8353,6 +8723,33 @@ THE SOFTWARE IS PROVIDED &quot;AS IS&quot; AND ISC DISCLAIMS ALL WARRANTIES WITH
### MIT License
```
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
```
### MIT License
```
Copyright (c) 2014 Carl Lerche and other MIO contributors
@@ -8467,6 +8864,30 @@ THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRES
### MIT License
```
Copyright (c) 2017 Gilad Naaman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
### MIT License
```
Copyright (c) 2017 h2 authors
@@ -8874,6 +9295,37 @@ THE SOFTWARE.
### MIT License
```
Copyright (c) 2024 Zeeshan Ali Khan &amp; zbus contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the &quot;Software&quot;), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
```
### MIT License
```
Copyright 2016 Nika Layzell
@@ -9089,6 +9541,35 @@ SOFTWARE.
### MIT License
```
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the &quot;Software&quot;), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
```
### MIT License
```
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the