Window Stacking Design

Problem Statement

In Symposium we wish to create the illusion of there being only one taskspace visible at a time. We achieve this by creating a "stack" of windows where the leader taskspace is on-top and the others ("followers") are underneath, making them invisible. The problem is that when the leader is moved, we need to ensure that the followers move as well or the illustion will be ruined. This document explores our design for achieving this in a smooth fashion.

Design Goals

  1. Single visible window: Only the active ("leader") window should be visible at any time
  2. Cohesive movement: When the leader window is moved, all stacked windows move together
  3. Reliable tracking: Changes in window position should be detected promptly and accurately
  4. Smooth transitions: Switching between windows in a stack should be fluid
  5. Minimal performance impact: Window tracking should not significantly impact system resources

Technical Approach

Leader/Follower Architecture

Each window stack consists of:

  • Leader window: The currently visible window that responds to user interaction
  • Follower windows: Hidden windows that track the leader's position

Only one window per stack acts as the leader at any time. All follower windows are positioned slightly inside the leader window's bounds to ensure they remain hidden even during movement lag.

Inset Positioning Strategy

Follower windows are positioned with a configurable inset relative to the leader window:

Leader window: (x, y, width, height)
Follower window: (x + inset, y + inset, width - 2*inset, height - 2*inset)

Default inset: 10% of window dimensions

Example calculation:

  • Leader at (100, 100) with size 1000×800
  • 10% inset = 100px horizontal, 80px vertical
  • Follower at (150, 140) with size 900×720

This inset serves multiple purposes:

  • Lag compensation: Even with 50-100ms notification delays, followers won't peek out during movement
  • Click protection: Prevents accidental interaction with hidden follower windows
  • Visual clarity: Makes the leader window unambiguously the active one

Movement Tracking with Event-Driven Polling

Note: This approach replaces the original AXObserver notification system which proved unreliable across macOS applications.

Window position tracking uses an AeroSpace-inspired event-driven polling system:

  1. CGEvent tap detects mouse clicks on any window system-wide
  2. Identify leader window by comparing clicked window with current stack leader
  3. Start timer-based polling (20ms interval) during drag operations
  4. Position delta calculation tracks leader movement and applies to followers
  5. Stop polling when drag operation completes

This approach provides:

  • 90%+ application compatibility (vs ~30% with AXObserver)
  • Minimal CPU overhead (polling only during active drags)
  • Low latency (20ms response time during movement)
  • Reliable detection across diverse application architectures

Leader Election and Handoff

When switching the active window in a stack:

  1. Stop position tracking for current leader window
  2. Resize and reposition old leader to follower dimensions/position
  3. Resize and reposition new leader to leader dimensions/position
  4. Raise new leader to top in window depth ordering
  5. Update leader reference for drag detection system

This handoff pattern ensures:

  • No conflicting position tracking during transitions
  • Clean separation between user-initiated and system-initiated position changes
  • Immediate activation of drag detection for new leader

Implementation Details

Configuration Options

ParameterDefaultRangeDescription
Inset percentage10%5-20%Follower window inset as percentage of leader size
Minimum inset10px5-50pxAbsolute minimum inset for very small windows
Maximum inset150px50-300pxAbsolute maximum inset for very large windows
Notification timeout200ms100-500msMax wait time for position updates

Edge Cases and Mitigations

Very Small Windows

  • Apply minimum absolute inset (10px) regardless of percentage
  • May result in minimal visual separation but preserves functionality

Very Large Windows

  • Apply maximum absolute inset (150px) to avoid excessive unused space
  • Maintains reasonable follower window usability

Manual Follower Movement

  • Followers moved manually (via Mission Control, etc.) are ignored
  • Position will be corrected on next leader switch
  • Alternative: Periodic position verification (future enhancement)

System-Initiated Movement

  • Display configuration changes may move all windows
  • Leader movement notifications will trigger follower repositioning
  • Natural recovery through normal tracking mechanism

Application Misbehavior

  • Some applications may resist programmatic repositioning
  • Error handling should gracefully exclude problematic windows from stacks
  • Logging available for debugging positioning failures

User Interface Integration

Stack Management Window

A separate monitoring window provides:

  • List of active stacks with window counts
  • Visual indication of current leader in each stack
  • Click-to-switch functionality
  • Future: Periodic thumbnail snapshots of stack contents

Visual Indicators

Current approach: Separate monitoring window Future possibilities:

  • Wider background window creating subtle drop shadow
  • Menu bar indicator with stack picker
  • Dock integration with notification badges

Performance Considerations

Event Detection Efficiency

  • Single CGEvent tap monitors system-wide mouse events
  • Event filtering occurs in kernel space for minimal overhead
  • Timer-based polling activated only during drag operations (typically 1-3 seconds)

Movement Latency

  • 20ms polling interval provides smooth 50fps tracking during drags
  • Sub-frame response time for typical window movements
  • Zero overhead when no drag operations are active

Memory Usage

  • Minimal overhead: notification observers and window position tracking
  • No additional window content rendering or capture required
  • Scales linearly with number of active stacks

Future Enhancements

Animated Transitions

  • Smooth resize/reposition animations during leader switches
  • Configurable animation duration and easing
  • May require Core Animation integration

Advanced Visual Indicators

  • Semi-transparent follower window previews
  • Stack depth indicators
  • Drag handles or manipulation widgets

Multi-Stack Management

  • Named stacks with persistence
  • Drag-and-drop between stacks
  • Keyboard shortcuts for stack navigation

Application Integration

  • VS Code extension for automatic window stacking
  • Terminal session integration
  • WebSocket API for programmatic control

Implementation Phases

Phase 1: Core Functionality ✓

  • Basic window stacking and switching
  • Manual add/remove from stacks
  • Simple position synchronization

Phase 2: Movement Tracking ✓

  • Implement kAXMovedNotification system (replaced with event-driven polling)
  • Implement AeroSpace-inspired drag detection with CGEvent taps
  • Add timer-based position tracking during active drags
  • Add inset positioning for followers
  • Create leader election and handoff logic

Phase 3: Enhanced UX

  • Stack monitoring window
  • Configuration options
  • Improved error handling and edge cases

Phase 4: Advanced Features

  • Animation system
  • Multiple stack support
  • External API integration

Success Criteria

The window stacking implementation will be considered successful when:

  1. Movement coherence: Dragging the leader window moves all followers seamlessly
  2. Visual isolation: Only the leader window is visible during normal operation
  3. Reliable switching: Users can switch between windows in a stack without position drift
  4. System stability: No performance impact or conflicts with macOS window management
  5. Edge case handling: Graceful behavior during display changes, app crashes, and unusual scenarios

This design provides a solid foundation for implementing true window stacking behavior while maintaining system compatibility and user experience quality.