feat(apple/ios): pen P4 — Apple Pencil (Pro) capture onto the stylus plane
The flagship leg of design/pen-tablet-input.md §7: against a HOST_CAP_PEN host the Pencil splits out of the finger path (independent of touch-input mode) into PencilStream — state-full samples with force→pressure, altitude→polar tilt, azimuth (+90° to the wire's north-clockwise convention), Pencil Pro rollAngle→ barrel roll (17.5+), coalesced touches batched ≤8/send for full 240Hz fidelity, and Pencil hover (zOffset>0 distinguishes it from trackpad hover) as in-range samples with distance. Squeeze holds barrel 1, double-tap clicks barrel 2 (the Pencil has no hardware buttons/eraser — these are how host apps get those affordances). Implements the ≤100ms heartbeat wire contract (80ms timer) so a stationary held stroke survives the host's 200ms dead-client failsafe. Toward a pen-less host nothing changes (Pencil stays a finger). Also fixes hostSupportsCursor testing the STALE 0x04 bit — HOST_CAP_CURSOR moved to 0x08 when TEXT_INPUT claimed 0x04; the old test would mistake a text-input host (e.g. Windows) for a cursor grant and double-render the pointer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -387,8 +387,16 @@ public final class PunktfunkConnection {
|
||||
|
||||
/// The host answered `HOST_CAP_CURSOR`: it stopped compositing the pointer and forwards
|
||||
/// shape/state on the cursor planes — the client MUST draw the cursor locally.
|
||||
/// `0x08` — the bit moved when `HOST_CAP_TEXT_INPUT` claimed `0x04` on main; testing the
|
||||
/// old bit would mistake a text-input-capable host (e.g. Windows) for a cursor grant.
|
||||
public var hostSupportsCursor: Bool {
|
||||
hostCaps & 0x04 != 0
|
||||
hostCaps & 0x08 != 0
|
||||
}
|
||||
|
||||
/// The host injects full-fidelity stylus input (`HOST_CAP_PEN`) — the gate for splitting
|
||||
/// Apple Pencil out of the touch path onto the pen plane (``sendPen(_:)``).
|
||||
public var hostSupportsPen: Bool {
|
||||
hostCaps & UInt8(PUNKTFUNK_HOST_CAP_PEN) != 0
|
||||
}
|
||||
|
||||
/// One forwarded host-cursor shape (the cursor channel, ABI v11): straight-alpha RGBA,
|
||||
@@ -1133,6 +1141,19 @@ public final class PunktfunkConnection {
|
||||
_ = punktfunk_connection_send_input(h, &ev)
|
||||
}
|
||||
|
||||
/// Send one stylus sample batch (≤ `PUNKTFUNK_PEN_BATCH_MAX`, oldest first) on the pen
|
||||
/// plane. Gate on ``hostSupportsPen`` — the core refuses toward a host without the cap.
|
||||
/// Thread-safe; silently dropped after close (input is lossy by design).
|
||||
public func sendPen(_ samples: [PunktfunkPenSample]) {
|
||||
guard !samples.isEmpty else { return }
|
||||
abiLock.lock()
|
||||
defer { abiLock.unlock() }
|
||||
guard let h = handle, !closeRequested else { return }
|
||||
samples.withUnsafeBufferPointer { buf in
|
||||
_ = punktfunk_connection_send_pen(h, buf.baseAddress, UInt32(buf.count))
|
||||
}
|
||||
}
|
||||
|
||||
/// Signal a **deliberate** user-initiated quit before ``close()``: the connection closes with
|
||||
/// `QUIT_CLOSE_CODE` (81) so the host tears the session down immediately instead of holding the
|
||||
/// keep-alive linger for a reconnect. Call only from an explicit "Disconnect" action — NOT from a
|
||||
|
||||
Reference in New Issue
Block a user