portico/Sources/Portico/MainLoop/SourceId.swift

69 lines
2.6 KiB
Swift

@_spi(SGTKInternal) import GLib
// MARK: - Raw GLib entry points
//
// gtk-swift's GLib wrapper exposes no binding for these, and `CGLib` is not a
// package product, so declare them directly - same idiom as
// Core/View+Conveniences.swift.
@_silgen_name("g_source_set_callback")
nonisolated func portico_g_source_set_callback(
_ source: UnsafeMutableRawPointer,
_ fn: @convention(c) (UnsafeMutableRawPointer?) -> Int32,
_ data: UnsafeMutableRawPointer?,
_ notify: (@convention(c) (UnsafeMutableRawPointer?) -> Void)?
)
@_silgen_name("g_source_set_priority")
nonisolated func portico_g_source_set_priority(
_ source: UnsafeMutableRawPointer, _ priority: Int32
)
@_silgen_name("g_source_attach")
nonisolated func portico_g_source_attach(
_ source: UnsafeMutableRawPointer, _ context: UnsafeMutableRawPointer?
) -> UInt32
@_silgen_name("g_source_destroy")
nonisolated func portico_g_source_destroy(_ source: UnsafeMutableRawPointer)
@_silgen_name("g_source_is_destroyed")
nonisolated func portico_g_source_is_destroyed(_ source: UnsafeMutableRawPointer) -> Int32
// MARK: - Source Id
/// A handle to a live GLib main-loop source created by ``Idle(priority:repeats:_:)``
/// or ``Timeout(interval:priority:repeats:_:)``.
///
/// Removal is idempotent and safe at any time, including from inside the
/// source's own callback: the handle retains the underlying `GSource` and
/// detaches it with `g_source_destroy`, so it never risks acting on a reissued
/// source tag the way `g_source_remove` does.
///
/// Letting the handle go out of scope does **not** remove the source - unlike
/// ``SubscriptionToken``, which cancels on release. Removal is always explicit,
/// or automatic via the appearance-scoped ``View/task(priority:_:)`` /
/// ``View/task(every:priority:_:)`` modifiers.
@MainActor public final class SourceId {
/// The GLib source tag (`g_source_get_id`), assigned when the source is
/// attached. Exposed for interop with raw GLib APIs only - use ``remove()``
/// to detach the source, never `g_source_remove(rawValue)`.
public internal(set) var rawValue: UInt32 = 0
private let source: GLib.Source
init(source: GLib.Source) { self.source = source }
/// `true` once the source is detached - either by ``remove()``, or by GLib
/// after a non-repeating callback returned.
public var isRemoved: Bool {
portico_g_source_is_destroyed(source.pointer) != 0
}
/// Detaches the source from the main loop. Idempotent; safe to call from
/// inside the source's own callback.
public func remove() {
guard !isRemoved else { return }
portico_g_source_destroy(source.pointer)
}
}