Remove environment variable features and add 4K resolution limit

- Remove VIBETUNNEL_FLIP_Y and VIBETUNNEL_USE_WARP environment variables
- Simplify coordinate transformation configuration
- Add automatic 4K resolution capping for displays above 4K (like 5K displays)
- Scale down proportionally to maintain aspect ratio (e.g., 5120×2880 → 3840×2160)
- Prevent web interface clipping on high-resolution displays
- Remove MOUSE_CLICK_DEBUG.md and test_display_coordinates.sh
- Fix linting issues in various files
This commit is contained in:
Peter Steinberger 2025-07-08 01:19:22 +01:00
parent 53b4aff571
commit 4fc7a5da5b
8 changed files with 39 additions and 118 deletions

View file

@ -1,82 +0,0 @@
# Mouse Click Debug Guide for VibeTunnel Screen Capture
## Problem
Mouse clicks are not working correctly in the screen capture feature. The coordinate system conversion might be incorrect.
## Changes Made
1. **Added Environment Variable Controls**:
- `VIBETUNNEL_FLIP_Y` - Set to "false" to disable Y-coordinate flipping
- `VIBETUNNEL_USE_WARP` - Set to "true" to use CGWarpMouseCursorPosition instead of CGEvent
2. **Enhanced Debug Logging**:
- Extensive coordinate transformation logging
- Screen information for all displays
- Mouse position before and after moves
- Capture mode and filter information
3. **Improved Mouse Movement**:
- Added mouse move before click (already implemented)
- Option to use CGWarpMouseCursorPosition for more direct cursor control
- Proper multi-monitor support with screen-relative Y-flipping
## Testing Instructions
### 1. Test Default Behavior (Y-flipping enabled)
```bash
./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
```
### 2. Test Without Y-Coordinate Flipping
```bash
VIBETUNNEL_FLIP_Y=false ./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
```
### 3. Test With CGWarpMouseCursorPosition
```bash
VIBETUNNEL_USE_WARP=true ./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
```
### 4. Test Both Options
```bash
VIBETUNNEL_FLIP_Y=false VIBETUNNEL_USE_WARP=true ./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
```
## What to Look For in Logs
Use `./scripts/vtlog.sh -f -c ScreencapService` to monitor logs and look for:
1. **Coordinate Transformation**:
- `🔍 [DEBUG] calculateClickLocation - Input: x=XXX, y=YYY`
- `🔍 [DEBUG] Configuration: shouldFlipY=true/false, useWarpCursor=true/false`
- `🔍 [DEBUG] Y-coordinate flipping DISABLED` (when VIBETUNNEL_FLIP_Y=false)
2. **Mouse Position**:
- `🖱️ Current mouse position: (XXX, YYY)`
- `🖱️ [DEBUG] Mouse position after move: (XXX, YYY)`
3. **Final Coordinates**:
- `🎯 [DEBUG] Final coordinates: x=XXX, y=YYY`
- `🔍 [DEBUG] Direct pixel coordinates (no Y-flip): x=XXX, y=YYY`
4. **Errors**:
- `❌ [DEBUG] CGWarpMouseCursorPosition failed`
- `🔐 [DEBUG] Accessibility permission status: false`
## Possible Issues
1. **Accessibility Permission**: Ensure VibeTunnel has accessibility permission in System Settings > Privacy & Security > Accessibility
2. **Coordinate Systems**:
- SCDisplay uses top-left origin (0,0 at top-left)
- NSScreen uses bottom-left origin (0,0 at bottom-left)
- The Y-flipping might not be needed if SCDisplay coordinates are already converted
3. **Multi-Monitor**: The code now properly handles multi-monitor setups by finding which screen contains the click point
## Next Steps
1. Test each configuration and observe where the mouse actually clicks
2. Check if clicks work better with `VIBETUNNEL_FLIP_Y=false`
3. Compare logged coordinates with expected click positions
4. If still not working, the debug logs will show exactly what coordinates are being used

View file

@ -278,26 +278,29 @@ public final class CaptureConfigurationBuilder {
return limitedDimensions
}
private func limitTo4K(width: Int, height: Int) -> (width: Int, height: Int) {
let max4KWidth = 3840
let max4KHeight = 2160
let max4KWidth = 3_840
let max4KHeight = 2_160
// If already within 4K bounds, return as is
if width <= max4KWidth && height <= max4KHeight {
return (width, height)
}
// Calculate scale factor to fit within 4K bounds while maintaining aspect ratio
let widthScale = Double(max4KWidth) / Double(width)
let heightScale = Double(max4KHeight) / Double(height)
let scale = min(widthScale, heightScale)
let scaledWidth = Int(Double(width) * scale)
let scaledHeight = Int(Double(height) * scale)
logger.info("🔽 Scaling down from \(width)x\(height) to \(scaledWidth)x\(scaledHeight) (scale: \(String(format: "%.2f", scale)))")
logger
.info(
"🔽 Scaling down from \(width)x\(height) to \(scaledWidth)x\(scaledHeight) (scale: \(String(format: "%.2f", scale)))"
)
return (width: scaledWidth, height: scaledHeight)
}

View file

@ -288,7 +288,6 @@ public final class CoordinateTransformer {
return CGPoint(x: clampedX, y: clampedY)
}
}
// MARK: - Helper Extensions

View file

@ -124,20 +124,20 @@ final class SystemPermissionManager {
requestPermission(permission)
}
}
/// Force a permission recheck (useful when user manually changes settings)
func forcePermissionRecheck() {
logger.info("Force permission recheck requested")
// Clear any cached values
permissions[.accessibility] = false
permissions[.screenRecording] = false
permissions[.appleScript] = false
// Immediate check
Task { @MainActor in
await checkAllPermissions()
// Double-check after a delay to catch any async updates
try? await Task.sleep(for: .milliseconds(500))
await checkAllPermissions()
@ -282,7 +282,7 @@ final class SystemPermissionManager {
// First check the API
let apiResult = AXIsProcessTrusted()
logger.debug("AXIsProcessTrusted returned: \(apiResult)")
// More comprehensive test - try to get focused application and its windows
// This definitely requires accessibility permission
let systemElement = AXUIElementCreateSystemWide()
@ -292,7 +292,7 @@ final class SystemPermissionManager {
kAXFocusedApplicationAttribute as CFString,
&focusedApp
)
if appResult == .success, let app = focusedApp {
// Try to get windows from the app - this definitely needs accessibility
var windows: CFTypeRef?
@ -301,10 +301,10 @@ final class SystemPermissionManager {
kAXWindowsAttribute as CFString,
&windows
)
let hasAccess = windowResult == .success
logger.debug("Comprehensive accessibility test result: \(hasAccess), can get windows: \(windows != nil)")
if hasAccess {
logger.debug("Accessibility permission verified through comprehensive test")
return true
@ -320,7 +320,7 @@ final class SystemPermissionManager {
logger.debug("API reports true but cannot access UI elements")
}
}
return false
}

View file

@ -46,7 +46,7 @@ struct AdvancedSettingsView: View {
.foregroundColor(.orange)
Text("VT update available")
.foregroundColor(.secondary)
Button("Update") {
Task {
await cliInstaller.install()

View file

@ -60,7 +60,7 @@ struct VTCommandPageView: View {
Text("CLI tool is outdated")
.foregroundColor(.secondary)
}
Button("Update VT Command Line Tool") {
Task {
await cliInstaller.install()

View file

@ -81,7 +81,7 @@ final class CLIInstaller {
isInstalled = isCorrectlyInstalled
logger.info("CLIInstaller: vt script installed: \(self.isInstalled)")
// If installed, check if it's outdated
if isInstalled {
checkScriptVersion()
@ -265,29 +265,29 @@ final class CLIInstaller {
alert.alertStyle = .critical
alert.runModal()
}
// MARK: - Script Version Detection
/// Calculates SHA256 hash of a file
private func calculateHash(for filePath: String) -> String? {
guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
return nil
}
let hash = SHA256.hash(data: data)
return hash.compactMap { String(format: "%02x", $0) }.joined()
}
/// Gets the hash of the bundled vt script
private func getBundledScriptHash() -> String? {
guard let scriptPath = Bundle.main.path(forResource: "vt", ofType: nil) else {
logger.error("CLIInstaller: Bundled vt script not found")
return nil
}
return calculateHash(for: scriptPath)
}
/// Checks if the installed script is outdated compared to bundled version
func checkScriptVersion() {
Task { @MainActor in
@ -295,13 +295,13 @@ final class CLIInstaller {
logger.error("CLIInstaller: Failed to get bundled script hash")
return
}
// Check both possible installation paths
let pathsToCheck = [
vtTargetPath,
"/opt/homebrew/bin/vt"
]
var installedHash: String?
for path in pathsToCheck where FileManager.default.fileExists(atPath: path) {
if let hash = calculateHash(for: path) {
@ -309,9 +309,9 @@ final class CLIInstaller {
break
}
}
// Update outdated status
if let installedHash = installedHash {
if let installedHash {
self.isOutdated = (installedHash != bundledHash)
logger.info("CLIInstaller: Script version check - outdated: \(self.isOutdated)")
logger.debug("CLIInstaller: Bundled hash: \(bundledHash), Installed hash: \(installedHash)")

View file

@ -197,15 +197,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser
// Check CLI installation status
let cliInstaller = CLIInstaller()
cliInstaller.checkInstallationStatus()
// Show welcome screen when version changes OR when vt script is outdated
let storedWelcomeVersion = UserDefaults.standard.integer(forKey: AppConstants.UserDefaultsKeys.welcomeVersion)
// Small delay to allow CLI check to complete
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
// Show welcome if version is different from current OR if vt script is outdated
if (storedWelcomeVersion < AppConstants.currentWelcomeVersion || cliInstaller.isOutdated)
&& !isRunningInTests && !isRunningInPreview {
if (storedWelcomeVersion < AppConstants.currentWelcomeVersion || cliInstaller.isOutdated)
&& !isRunningInTests && !isRunningInPreview
{
self?.showWelcomeScreen()
}
}