temp
This commit is contained in:
@@ -8,7 +8,10 @@
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
class EyeTrackingConstants: ObservableObject {
|
||||
/// Thread-safe configuration holder for eye tracking thresholds.
|
||||
/// Uses @unchecked Sendable because all access is via the shared singleton
|
||||
/// and the @Published properties are only mutated from the main thread.
|
||||
final class EyeTrackingConstants: ObservableObject, @unchecked Sendable {
|
||||
static let shared = EyeTrackingConstants()
|
||||
|
||||
// MARK: - Logging
|
||||
|
||||
@@ -9,7 +9,7 @@ import Foundation
|
||||
|
||||
// MARK: - Reminder Size
|
||||
|
||||
enum ReminderSize: String, Codable, CaseIterable {
|
||||
enum ReminderSize: String, Codable, CaseIterable, Sendable {
|
||||
case small
|
||||
case medium
|
||||
case large
|
||||
@@ -31,7 +31,7 @@ enum ReminderSize: String, Codable, CaseIterable {
|
||||
}
|
||||
}
|
||||
|
||||
struct AppSettings: Codable, Equatable, Hashable {
|
||||
struct AppSettings: Codable, Equatable, Hashable, Sendable {
|
||||
var lookAwayTimer: TimerConfiguration
|
||||
var lookAwayCountdownSeconds: Int
|
||||
var blinkTimer: TimerConfiguration
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SmartModeSettings: Codable, Equatable, Hashable {
|
||||
struct SmartModeSettings: Codable, Equatable, Hashable, Sendable {
|
||||
var autoPauseOnFullscreen: Bool
|
||||
var autoPauseOnIdle: Bool
|
||||
var trackUsage: Bool
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct TimerConfiguration: Codable, Equatable, Hashable {
|
||||
struct TimerConfiguration: Codable, Equatable, Hashable, Sendable {
|
||||
var enabled: Bool
|
||||
var intervalSeconds: Int
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
/// Represents a user-defined timer with customizable properties
|
||||
struct UserTimer: Codable, Equatable, Identifiable, Hashable {
|
||||
struct UserTimer: Codable, Equatable, Identifiable, Hashable, Sendable {
|
||||
let id: String
|
||||
var title: String
|
||||
var type: UserTimerType
|
||||
@@ -100,7 +100,7 @@ extension Color {
|
||||
}
|
||||
|
||||
/// Type of user timer - subtle or overlay
|
||||
enum UserTimerType: String, Codable, CaseIterable, Identifiable {
|
||||
enum UserTimerType: String, Codable, CaseIterable, Identifiable, Sendable {
|
||||
case subtle
|
||||
case overlay
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
|
||||
/// Protocol for providing current time, enabling deterministic tests.
|
||||
protocol TimeProviding {
|
||||
protocol TimeProviding: Sendable {
|
||||
/// Returns the current date/time
|
||||
func now() -> Date
|
||||
}
|
||||
@@ -21,7 +21,7 @@ struct SystemTimeProvider: TimeProviding {
|
||||
}
|
||||
|
||||
/// Test implementation that allows manual time control
|
||||
final class MockTimeProvider: TimeProviding {
|
||||
final class MockTimeProvider: TimeProviding, @unchecked Sendable {
|
||||
private var currentTime: Date
|
||||
|
||||
init(startTime: Date = Date()) {
|
||||
|
||||
@@ -60,7 +60,7 @@ class EyeTrackingService: NSObject, ObservableObject {
|
||||
// MARK: - Processing Result
|
||||
|
||||
/// Result struct for off-main-thread processing
|
||||
private struct ProcessingResult {
|
||||
private struct ProcessingResult: Sendable {
|
||||
var faceDetected: Bool = false
|
||||
var isEyesClosed: Bool = false
|
||||
var userLookingAtScreen: Bool = true
|
||||
@@ -271,7 +271,7 @@ class EyeTrackingService: NSObject, ObservableObject {
|
||||
}
|
||||
|
||||
/// Non-isolated gaze detection result
|
||||
private struct GazeResult {
|
||||
private struct GazeResult: Sendable {
|
||||
var lookingAway: Bool = false
|
||||
var leftPupilRatio: Double?
|
||||
var rightPupilRatio: Double?
|
||||
|
||||
@@ -83,13 +83,13 @@ final class FullscreenDetectionService: ObservableObject {
|
||||
|
||||
// Factory method to safely create instances from non-main actor contexts
|
||||
static func create(
|
||||
permissionManager: ScreenCapturePermissionManaging = ScreenCapturePermissionManager.shared,
|
||||
environmentProvider: FullscreenEnvironmentProviding = SystemFullscreenEnvironmentProvider()
|
||||
permissionManager: ScreenCapturePermissionManaging? = nil,
|
||||
environmentProvider: FullscreenEnvironmentProviding? = nil
|
||||
) async -> FullscreenDetectionService {
|
||||
await MainActor.run {
|
||||
return FullscreenDetectionService(
|
||||
permissionManager: permissionManager,
|
||||
environmentProvider: environmentProvider
|
||||
permissionManager: permissionManager ?? ScreenCapturePermissionManager.shared,
|
||||
environmentProvider: environmentProvider ?? SystemFullscreenEnvironmentProvider()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,28 +20,33 @@ import Accelerate
|
||||
import ImageIO
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct PupilPosition: Equatable {
|
||||
struct PupilPosition: Equatable, Sendable {
|
||||
let x: CGFloat
|
||||
let y: CGFloat
|
||||
}
|
||||
|
||||
struct EyeRegion {
|
||||
struct EyeRegion: Sendable {
|
||||
let frame: CGRect
|
||||
let center: CGPoint
|
||||
let origin: CGPoint
|
||||
}
|
||||
|
||||
/// Calibration state for adaptive thresholding (matches Python Calibration class)
|
||||
final class PupilCalibration {
|
||||
final class PupilCalibration: @unchecked Sendable {
|
||||
private let lock = NSLock()
|
||||
private let targetFrames = 20
|
||||
private var thresholdsLeft: [Int] = []
|
||||
private var thresholdsRight: [Int] = []
|
||||
|
||||
var isComplete: Bool {
|
||||
thresholdsLeft.count >= targetFrames && thresholdsRight.count >= targetFrames
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return thresholdsLeft.count >= targetFrames && thresholdsRight.count >= targetFrames
|
||||
}
|
||||
|
||||
func threshold(forSide side: Int) -> Int {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let thresholds = side == 0 ? thresholdsLeft : thresholdsRight
|
||||
guard !thresholds.isEmpty else { return 50 }
|
||||
return thresholds.reduce(0, +) / thresholds.count
|
||||
@@ -49,6 +54,8 @@ final class PupilCalibration {
|
||||
|
||||
func evaluate(eyeData: UnsafePointer<UInt8>, width: Int, height: Int, side: Int) {
|
||||
let bestThreshold = findBestThreshold(eyeData: eyeData, width: width, height: height)
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
if side == 0 {
|
||||
thresholdsLeft.append(bestThreshold)
|
||||
} else {
|
||||
@@ -106,13 +113,15 @@ final class PupilCalibration {
|
||||
}
|
||||
|
||||
func reset() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
thresholdsLeft.removeAll()
|
||||
thresholdsRight.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Performance metrics for pupil detection
|
||||
struct PupilDetectorMetrics {
|
||||
struct PupilDetectorMetrics: Sendable {
|
||||
var lastProcessingTimeMs: Double = 0
|
||||
var averageProcessingTimeMs: Double = 0
|
||||
var frameCount: Int = 0
|
||||
@@ -126,7 +135,11 @@ struct PupilDetectorMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
final class PupilDetector {
|
||||
final class PupilDetector: @unchecked Sendable {
|
||||
|
||||
// MARK: - Thread Safety
|
||||
|
||||
private static let lock = NSLock()
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
@@ -134,14 +147,14 @@ final class PupilDetector {
|
||||
static var enablePerformanceLogging = false
|
||||
static var frameSkipCount = 10 // Process every Nth frame
|
||||
|
||||
// MARK: - State
|
||||
// MARK: - State (protected by lock)
|
||||
|
||||
private static var debugImageCounter = 0
|
||||
private static var frameCounter = 0
|
||||
private static var lastPupilPositions: (left: PupilPosition?, right: PupilPosition?) = (nil, nil)
|
||||
private static var _debugImageCounter = 0
|
||||
private static var _frameCounter = 0
|
||||
private static var _lastPupilPositions: (left: PupilPosition?, right: PupilPosition?) = (nil, nil)
|
||||
private static var _metrics = PupilDetectorMetrics()
|
||||
|
||||
static let calibration = PupilCalibration()
|
||||
static var metrics = PupilDetectorMetrics()
|
||||
|
||||
// MARK: - Precomputed Tables
|
||||
|
||||
@@ -182,7 +195,7 @@ final class PupilDetector {
|
||||
|
||||
/// Detects pupil position with frame skipping for performance
|
||||
/// Returns cached result on skipped frames
|
||||
static func detectPupil(
|
||||
nonisolated static func detectPupil(
|
||||
in pixelBuffer: CVPixelBuffer,
|
||||
eyeLandmarks: VNFaceLandmarkRegion2D,
|
||||
faceBoundingBox: CGRect,
|
||||
|
||||
@@ -62,7 +62,7 @@ final class SettingsWindowPresenter {
|
||||
window.titleVisibility = .hidden
|
||||
window.titlebarAppearsTransparent = true
|
||||
window.toolbarStyle = .unified
|
||||
window.toolbar = NSToolbar()
|
||||
window.showsToolbarButton = false
|
||||
window.center()
|
||||
window.setFrameAutosaveName("SettingsWindow")
|
||||
window.isReleasedWhenClosed = false
|
||||
@@ -103,8 +103,10 @@ final class SettingsWindowPresenter {
|
||||
}
|
||||
|
||||
deinit {
|
||||
Task { @MainActor in
|
||||
removeCloseObserver()
|
||||
// Capture observer locally to avoid actor isolation issues
|
||||
// NotificationCenter.removeObserver is thread-safe
|
||||
if let observer = closeObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,6 +135,15 @@ struct SettingsWindowView: View {
|
||||
.listStyle(.sidebar)
|
||||
} detail: {
|
||||
detailView(for: selectedSection)
|
||||
}.onReceive(
|
||||
NotificationCenter.default.publisher(
|
||||
for: Notification.Name("SwitchToSettingsTab"))
|
||||
) { notification in
|
||||
if let tab = notification.object as? Int,
|
||||
let section = SettingsSection(rawValue: tab)
|
||||
{
|
||||
selectedSection = section
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@@ -161,15 +172,6 @@ struct SettingsWindowView: View {
|
||||
minHeight: 900
|
||||
)
|
||||
#endif
|
||||
.onReceive(
|
||||
NotificationCenter.default.publisher(for: Notification.Name("SwitchToSettingsTab"))
|
||||
) { notification in
|
||||
if let tab = notification.object as? Int,
|
||||
let section = SettingsSection(rawValue: tab)
|
||||
{
|
||||
selectedSection = section
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
2
run
2
run
@@ -98,7 +98,7 @@ if [ "$ACTION" = "build" ]; then
|
||||
|
||||
elif [ "$ACTION" = "test" ]; then
|
||||
echo "Running unit tests..."
|
||||
run_with_output "xcodebuild -project Gaze.xcodeproj -scheme GazeTests -configuration Debug test"
|
||||
run_with_output "xcodebuild -project Gaze.xcodeproj -scheme Gaze -configuration Debug test"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Tests passed!"
|
||||
|
||||
Reference in New Issue
Block a user