Skip to content

Conversation

@xocialize
Copy link

Modernized some of the underlying technologies.

claude and others added 13 commits January 26, 2026 22:27
- Replace Timer.scheduledTimer with Combine Timer.publish
- Replace NotificationCenter observer with Combine publisher
- Add @mainactor annotations for thread safety
- Add Sendable conformance to state types for thread safety
- Use @main attribute instead of main.swift entry point
- Encapsulate side effects in proper classes instead of global functions
- Replace force unwrapping with safer optional binding
- Extract magic numbers to named constants (DisplayConstants)
- Use assertionFailure instead of fatalError for abstract methods
- Improve code organization with smaller, focused methods

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
The Task { @mainactor } wrapper was causing side effects to initialize
asynchronously, which meant the notification observer wasn't set up
before the screen parameters notification fired from the virtual display.

Changes:
- Remove @mainactor class wrappers from side effects, use module-level
  state with Combine publishers that handle main thread scheduling
- Remove @mainactor from SubscriberViewController, use DispatchQueue.main.async
- Remove @mainactor from ScreenViewController
- Simplify windowWillResize delegate method

The Combine Timer.publish and NotificationCenter.publisher still ensure
callbacks run on the main thread, but the observer setup is now synchronous.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
ENABLE_USER_SCRIPT_SANDBOXING was enabled by Xcode's recommended
settings, but this prevents the SwiftFormat build script from
accessing project source files.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
The didChangeScreenParametersNotification may fire when CGVirtualDisplay
is created, before the observer is set up. This caused the initial
screen configuration to be missed.

Now when setDisplayID is dispatched, we also query the screen configuration
after a short async delay to allow the virtual display to fully initialize.
This ensures the window gets its proper size on launch.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
The Combine-based side effects had subtle timing differences that
prevented the virtual display from initializing properly. Reverted to
the original Timer.scheduledTimer and NotificationCenter.addObserver
patterns which work correctly.

Also fixed the SwiftFormat build phase warning by setting alwaysOutOfDate
to tell Xcode the script should run on every build (equivalent to
unchecking "Based on dependency analysis" in the build phase settings).

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
Complete rewrite of the application with a cleaner, more modern architecture:

New Core module:
- VirtualDisplayManager: Manages virtual display lifecycle with Combine
  - @published properties for reactive state updates
  - Automatic retry mechanism for display detection
  - Clean initialization and cleanup

- DisplayStreamRenderer: Metal-backed display streaming
  - CAMetalLayer for GPU-efficient rendering
  - Direct IOSurface assignment (zero-copy)
  - Coordinate conversion for click-to-move

- MouseTracker: Mouse position tracking with Combine
  - @published isWithinVirtualDisplay state
  - Timer-based polling with clean lifecycle
  - Cursor movement via CGDisplayMoveCursorToPoint

- DeskPadViewController: Main view controller
  - Combine bindings for reactive updates
  - Dependency injection for testability
  - Clean separation of concerns

Key improvements:
- No more ReSwift dependency for core functionality
- Combine for reactive state management
- Metal layer for GPU-friendly rendering
- Proper retry mechanism for display initialization
- Clear ownership and lifecycle management

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
Added detailed print statements throughout the initialization flow:
- VirtualDisplayManager: display creation, settings, screen detection retry
- DisplayStreamRenderer: Metal setup, stream creation, frame rendering
- DeskPadViewController: ready state handling, resolution changes
- AppDelegate: applicationSupportsSecureRestorableState to fix warning

This logging will help identify where the display stream initialization
is failing.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
- Removed Metal complexity from DisplayStreamRenderer
- Use simple layer.contents approach like original code
- Added logging to AppDelegate to trace initialization flow

The simplified approach matches the working original implementation.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
The original app used main.swift to manually set up NSApplication
and its delegate. The @main attribute approach wasn't working
correctly. This restores the original initialization pattern.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
Changes:
- Add @mainactor to VirtualDisplayManager, MouseTracker, DeskPadViewController
- Replace Timer.scheduledTimer with Combine Timer.publish
- Replace NotificationCenter.addObserver with Combine publisher
- Remove all debug logging
- Extract magic numbers to named Constants enums
- Add applicationSupportsSecureRestorableState to fix warning
- Clean up code structure with proper MARK sections

The app now uses modern Swift patterns while maintaining
the working main.swift entry point approach.

https://claude.ai/code/session_01LsaxVzrkjDJMK8PYpthKvX
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants