Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.5.6</string>
<string>0.5.7</string>
<key>CFBundleVersion</key>
<string>66</string>
<string>67</string>
<key>LSMinimumSystemVersion</key>
<string>26.0</string>
<key>LSUIElement</key>
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: format format-check lint lint-fix build run release-check verify check check-tool-versions check-swiftformat-version check-swiftlint-version

SWIFTFORMAT_VERSION = 0.61.1
SWIFTFORMAT_VERSION = 0.62.1
SWIFTLINT_VERSION = 0.65.0
SWIFT_WITH_GHOSTTY = LIBRARY_PATH="$$(./Scripts/ghostty-preflight.sh print-library-dir)$${LIBRARY_PATH:+:$$LIBRARY_PATH}"

Expand Down
4 changes: 2 additions & 2 deletions Scripts/package-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ GHOSTTY_LIBRARY_DIR="$("$ROOT_DIR/Scripts/ghostty-preflight.sh" print-library-di
SWIFT_BUILD_ARGS=(-c "$CONFIG" --arch arm64)

# Signing identity and notarization profile
SIGNING_IDENTITY="Developer ID Application: Oliver Nikolic (VF8LDJRGFM)"
NOTARIZE_PROFILE="OmniWM-Notarize"
SIGNING_IDENTITY="${OMNIWM_SIGNING_IDENTITY:-Developer ID Application: Oliver Nikolic (VF8LDJRGFM)}"
NOTARIZE_PROFILE="${OMNIWM_NOTARIZE_PROFILE:-OmniWM-Notarize}"
ENTITLEMENTS="$ROOT_DIR/OmniWM.entitlements"

echo "Running release checks..."
Expand Down
59 changes: 47 additions & 12 deletions Sources/OmniWM/Core/Ax/AXCallbackGenerationRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,84 @@
import Synchronization

final class AXCallbackGenerationRegistry: Sendable {
private struct ObserverState {
let serviceGeneration: UInt64
let callbackGeneration: UInt64
var isActive: Bool
}

private struct State {
var generation: UInt64 = 1
var observerGenerations: [UInt: UInt64] = [:]
var serviceGeneration: UInt64 = 1
var nextCallbackGeneration: UInt64 = 1
var observers: [UInt: ObserverState] = [:]
}

private let state = Mutex(State())

var currentGeneration: UInt64 {
state.withLock { $0.generation }
state.withLock { $0.serviceGeneration }
}

func isCurrent(_ generation: UInt64) -> Bool {
state.withLock { $0.generation == generation }
state.withLock { $0.serviceGeneration == generation }
}

func reserveCallbackGeneration(serviceGeneration: UInt64) -> UInt64? {
state.withLock {
guard $0.serviceGeneration == serviceGeneration else { return nil }
let generation = $0.nextCallbackGeneration
$0.nextCallbackGeneration &+= 1
return generation
}
}

@discardableResult
func register(observerKey: UInt, generation: UInt64) -> Bool {
func register(
observerKey: UInt,
serviceGeneration: UInt64,
callbackGeneration: UInt64
) -> Bool {
state.withLock {
guard $0.generation == generation else { return false }
$0.observerGenerations[observerKey] = generation
guard $0.serviceGeneration == serviceGeneration else { return false }
$0.observers[observerKey] = ObserverState(
serviceGeneration: serviceGeneration,
callbackGeneration: callbackGeneration,
isActive: true
)
return true
}
}

func unregister(observerKey: UInt) {
state.withLock { state in
_ = state.observerGenerations.removeValue(forKey: observerKey)
state.observers[observerKey]?.isActive = false
}
}

func generation(observerKey: UInt) -> UInt64? {
state.withLock { $0.observers[observerKey]?.callbackGeneration }
}

@discardableResult
func advance() -> UInt64 {
state.withLock {
$0.generation &+= 1
$0.observerGenerations.removeAll(keepingCapacity: true)
return $0.generation
$0.serviceGeneration &+= 1
for key in Array($0.observers.keys) {
$0.observers[key]?.isActive = false
}
return $0.serviceGeneration
}
}

@discardableResult
func performIfCurrent(observerKey: UInt, _ operation: () -> Void) -> Bool {
state.withLock {
guard $0.observerGenerations[observerKey] == $0.generation else { return false }
guard let observer = $0.observers[observerKey],
observer.isActive,
observer.serviceGeneration == $0.serviceGeneration
else {
return false
}
operation()
return true
}
Expand Down
34 changes: 23 additions & 11 deletions Sources/OmniWM/Core/Ax/AXFrameApplicationLedger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct AXFrameTerminalRefusal: Equatable {
let windowId: Int
let targetFrame: CGRect
let observedFrame: CGRect
let failureReason: AXFrameWriteFailureReason
}

struct AXFrameEnqueueDecision {
Expand Down Expand Up @@ -99,7 +100,9 @@ final class AXFrameApplicationLedger {
var parts = ["win=\(windowId)"]
if let frame = lastAppliedFrames[windowId] { parts.append("lastApplied=\(TraceFormat.rect(frame))") }
if let pending = pendingFrameWrites[windowId] { parts.append("pending=\(TraceFormat.rect(pending))") }
if let failure = recentFrameWriteFailures[windowId] { parts.append("failure=\(failure)") }
if let failure = recentFrameWriteFailures[windowId] {
parts.append("failure=\(failure.traceDescription)")
}
if let retry = retryBudgetByWindowId[windowId] { parts.append("retryBudget=\(retry)") }
return parts.joined(separator: " ")
}
Expand Down Expand Up @@ -316,7 +319,9 @@ final class AXFrameApplicationLedger {
let requestId = makeNextFrameApplicationRequestId()
pendingFrameWrites[windowId] = frame
pendingFrameRequestIdByWindowId[windowId] = requestId
recentFrameWriteFailures.removeValue(forKey: windowId)
if !isRetry {
recentFrameWriteFailures.removeValue(forKey: windowId)
}
if let existingObserverRequestId,
var pendingObserver = pendingFrameObserversByRequestId[existingObserverRequestId],
pendingObserver.targetFrame.approximatelyEqual(to: frame, tolerance: FrameTolerance.frameWrite)
Expand Down Expand Up @@ -355,7 +360,10 @@ final class AXFrameApplicationLedger {
)
}

func handleFrameApplyResults(_ results: [AXFrameApplyResult]) -> AXFrameApplyOutcome {
func handleFrameApplyResults(
_ results: [AXFrameApplyResult],
onAcceptedSuccess: (AXFrameApplyResult) -> Void = { _ in }
) -> AXFrameApplyOutcome {
var outcome = AXFrameApplyOutcome()
for result in results {
let resolvedWindowId = resolveWindowId(for: result.windowId)
Expand All @@ -381,6 +389,7 @@ final class AXFrameApplicationLedger {
}
recentFrameWriteFailures.removeValue(forKey: resolvedWindowId)
retryBudgetByWindowId.removeValue(forKey: resolvedWindowId)
onAcceptedSuccess(resolvedResult)
outcome.deliveries.append(contentsOf: notifyPendingFrameObserver(with: resolvedResult))
clearSettledRekeyMappings(to: resolvedWindowId)
continue
Expand All @@ -399,23 +408,26 @@ final class AXFrameApplicationLedger {
)
else {
retryBudgetByWindowId.removeValue(forKey: resolvedWindowId)
if resolvedResult.writeResult.failureReason == .verificationMismatch,
priorFailureReason == .verificationMismatch,
if let failureReason = resolvedResult.writeResult.failureReason,
priorFailureReason == failureReason,
let observedFrame = resolvedResult.writeResult.observedFrame
{
outcome.terminalRefusals.append(
AXFrameTerminalRefusal(
pid: resolvedResult.pid,
windowId: resolvedWindowId,
targetFrame: resolvedResult.targetFrame,
observedFrame: observedFrame
observedFrame: observedFrame,
failureReason: failureReason
)
)
learnSizeQuantum(
windowId: resolvedWindowId,
target: resolvedResult.targetFrame,
observed: observedFrame
)
if failureReason == .verificationMismatch {
learnSizeQuantum(
windowId: resolvedWindowId,
target: resolvedResult.targetFrame,
observed: observedFrame
)
}
}
outcome.deliveries.append(contentsOf: notifyPendingFrameObserver(with: resolvedResult))
clearSettledRekeyMappings(to: resolvedWindowId)
Expand Down
Loading