62 lines
2.9 KiB
Swift
62 lines
2.9 KiB
Swift
|
|
// VoiceCatEvent — a Swift value type that is safe to hold/queue past the native callback's
|
||
|
|
// return. This is the Swift analog of the C# client's `VoiceCatEvent` record.
|
||
|
|
//
|
||
|
|
// CRITICAL (voicecat.h's vc_event doc comment): the native `vc_event.text` pointer is owned
|
||
|
|
// by the core and valid ONLY for the duration of the `on_event` callback. `from(_:)` copies
|
||
|
|
// it to a Swift `String` immediately — never hold the raw `vc_event` across the callback
|
||
|
|
// boundary, or `text` will be a dangling pointer by the time it's read. This is the #1
|
||
|
|
// lifetime rule carried over from the Windows client (NativeCallbacks.cs / VoiceCatEvent.cs).
|
||
|
|
|
||
|
|
import VoiceCatC
|
||
|
|
|
||
|
|
/// A Swift-safe copy of a `vc_event`. Produced inside the `on_event` callback (see
|
||
|
|
/// Callbacks.swift) — all pointer fields are converted to value types before the callback
|
||
|
|
/// returns.
|
||
|
|
public struct VoiceCatEvent: Sendable, Equatable {
|
||
|
|
public let type: VoiceCatEventType
|
||
|
|
public let connectionState: VoiceCatConnectionState
|
||
|
|
public let result: VoiceCatResult
|
||
|
|
public let userId: UInt32
|
||
|
|
public let channelId: UInt32
|
||
|
|
public let streamId: UInt32
|
||
|
|
public let textScope: VoiceCatTextScope
|
||
|
|
/// Generic small payload, meaning per event type. For `.serverIdentity` this is the
|
||
|
|
/// `VoiceCatTofuStatus`; for `.genericResult` the server error code; for `.talkState`
|
||
|
|
/// talking(0/1).
|
||
|
|
public let u32a: UInt32
|
||
|
|
/// Copied from the core's `vc_event.text` inside the callback. nil if the core passed NULL.
|
||
|
|
public let text: String?
|
||
|
|
public let timestampUnixMs: UInt64
|
||
|
|
|
||
|
|
/// Convenience: the TOFU status, valid when `type == .serverIdentity` (maps `u32a`).
|
||
|
|
public var tofuStatus: VoiceCatTofuStatus? {
|
||
|
|
type == .serverIdentity ? VoiceCatTofuStatus(rawValue: u32a) : nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Copy a native `vc_event` into a safe Swift value. MUST be called inside the callback
|
||
|
|
/// while `ev.text` is still valid — `String(cString:)` copies the bytes here.
|
||
|
|
@inline(__always)
|
||
|
|
public static func from(_ ev: vc_event) -> VoiceCatEvent {
|
||
|
|
let text: String?
|
||
|
|
if let raw = ev.text {
|
||
|
|
text = String(cString: raw) // copies — safe to hold past callback return
|
||
|
|
} else {
|
||
|
|
text = nil
|
||
|
|
}
|
||
|
|
// ev.result is int32_t (not vc_result) per voicecat.h — bridge via bitPattern.
|
||
|
|
// ev.u32a is uint32_t — matches VoiceCatTofuStatus's UInt32 raw value directly.
|
||
|
|
return VoiceCatEvent(
|
||
|
|
type: VoiceCatEventType(ev.type),
|
||
|
|
connectionState: VoiceCatConnectionState(ev.connection_state),
|
||
|
|
result: VoiceCatResult(rawValue: UInt32(bitPattern: ev.result)) ?? .internalError,
|
||
|
|
userId: ev.user_id,
|
||
|
|
channelId: ev.channel_id,
|
||
|
|
streamId: ev.stream_id,
|
||
|
|
textScope: VoiceCatTextScope(ev.text_scope),
|
||
|
|
u32a: ev.u32a,
|
||
|
|
text: text,
|
||
|
|
timestampUnixMs: ev.timestamp_unix_ms
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|