- Use full screen capture instead of active window (more reliable) - Explicitly preserve and set DISPLAY environment variable - Default to :0 if DISPLAY not set - Better error logging with stderr output - Use time.time() instead of date command for timestamp Fixes screenshot failures when DISPLAY not inherited from parent process. Changes from window-specific capture to full screen for better reliability. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Jean-Philippe Brule <jp@svrnty.io>
140 lines
4.1 KiB
Python
140 lines
4.1 KiB
Python
"""
|
|
Screenshot capture functionality
|
|
"""
|
|
|
|
import subprocess
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from . import config
|
|
|
|
|
|
class ScreenshotError(Exception):
|
|
"""Raised when screenshot capture fails"""
|
|
pass
|
|
|
|
|
|
def find_screenshot_tool() -> Optional[str]:
|
|
"""Find available screenshot tool on the system"""
|
|
for tool in config.SCREENSHOT_TOOLS:
|
|
if shutil.which(tool):
|
|
return tool
|
|
return None
|
|
|
|
|
|
def take_screenshot() -> Optional[str]:
|
|
"""
|
|
Take screenshot of full screen
|
|
|
|
Returns:
|
|
Path to screenshot file, or None if capture failed
|
|
"""
|
|
import os
|
|
import time
|
|
|
|
tool = find_screenshot_tool()
|
|
|
|
if not tool:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] No screenshot tool found. Tried: {', '.join(config.SCREENSHOT_TOOLS)}")
|
|
return None
|
|
|
|
# Create temporary file
|
|
cache_dir = config.get_cache_dir()
|
|
screenshot_path = cache_dir / f"screenshot_{int(time.time())}.png"
|
|
|
|
# Preserve DISPLAY environment variable
|
|
env = os.environ.copy()
|
|
if 'DISPLAY' not in env:
|
|
env['DISPLAY'] = ':0' # Default X display
|
|
|
|
try:
|
|
if tool == "scrot":
|
|
# Capture full screen (more reliable than -u for active window)
|
|
result = subprocess.run(
|
|
["scrot", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT,
|
|
env=env
|
|
)
|
|
elif tool == "gnome-screenshot":
|
|
# Capture full screen
|
|
result = subprocess.run(
|
|
["gnome-screenshot", "-f", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT,
|
|
env=env
|
|
)
|
|
elif tool == "import":
|
|
# ImageMagick - capture root window
|
|
result = subprocess.run(
|
|
["import", "-window", "root", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT,
|
|
env=env
|
|
)
|
|
elif tool == "maim":
|
|
# Capture full screen
|
|
result = subprocess.run(
|
|
["maim", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT,
|
|
env=env
|
|
)
|
|
else:
|
|
return None
|
|
|
|
if screenshot_path.exists():
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Screenshot saved to {screenshot_path}")
|
|
return str(screenshot_path)
|
|
else:
|
|
return None
|
|
|
|
except subprocess.TimeoutExpired as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Screenshot timeout with tool: {tool}")
|
|
return None
|
|
except subprocess.CalledProcessError as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Screenshot failed with {tool}: {e}")
|
|
if e.stderr:
|
|
print(f"[DEBUG] Error output: {e.stderr.decode('utf-8', errors='ignore')}")
|
|
return None
|
|
except Exception as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Unexpected screenshot error: {type(e).__name__}: {e}")
|
|
return None
|
|
|
|
|
|
def cleanup_old_screenshots(max_age_seconds: int = None):
|
|
"""
|
|
Clean up old screenshots from cache directory
|
|
|
|
Args:
|
|
max_age_seconds: Maximum age of screenshots to keep (default from config)
|
|
"""
|
|
import time
|
|
|
|
if max_age_seconds is None:
|
|
max_age_seconds = config.SCREENSHOT_CACHE_CLEANUP
|
|
|
|
cache_dir = config.get_cache_dir()
|
|
current_time = time.time()
|
|
|
|
for screenshot in cache_dir.glob("screenshot_*.png"):
|
|
if current_time - screenshot.stat().st_mtime > max_age_seconds:
|
|
try:
|
|
screenshot.unlink()
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Cleaned up old screenshot: {screenshot}")
|
|
except Exception as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Failed to cleanup {screenshot}: {e}")
|