fix: Screenshot capture with DISPLAY env and full screen

- 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>
This commit is contained in:
Svrnty 2025-10-30 02:17:12 -04:00
parent 29ac2f0929
commit 5ea9928924

View File

@ -26,11 +26,14 @@ def find_screenshot_tool() -> Optional[str]:
def take_screenshot() -> Optional[str]: def take_screenshot() -> Optional[str]:
""" """
Take screenshot of active terminal window Take screenshot of full screen
Returns: Returns:
Path to screenshot file, or None if capture failed Path to screenshot file, or None if capture failed
""" """
import os
import time
tool = find_screenshot_tool() tool = find_screenshot_tool()
if not tool: if not tool:
@ -40,41 +43,49 @@ def take_screenshot() -> Optional[str]:
# Create temporary file # Create temporary file
cache_dir = config.get_cache_dir() cache_dir = config.get_cache_dir()
screenshot_path = cache_dir / f"screenshot_{int(subprocess.check_output(['date', '+%s']).decode().strip())}.png" 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: try:
if tool == "scrot": if tool == "scrot":
# Capture active window # Capture full screen (more reliable than -u for active window)
subprocess.run( result = subprocess.run(
["scrot", "-u", str(screenshot_path)], ["scrot", str(screenshot_path)],
check=True, check=True,
capture_output=True, capture_output=True,
timeout=config.SCREENSHOT_TIMEOUT timeout=config.SCREENSHOT_TIMEOUT,
env=env
) )
elif tool == "gnome-screenshot": elif tool == "gnome-screenshot":
# Capture active window # Capture full screen
subprocess.run( result = subprocess.run(
["gnome-screenshot", "-w", "-f", str(screenshot_path)], ["gnome-screenshot", "-f", str(screenshot_path)],
check=True, check=True,
capture_output=True, capture_output=True,
timeout=config.SCREENSHOT_TIMEOUT timeout=config.SCREENSHOT_TIMEOUT,
env=env
) )
elif tool == "import": elif tool == "import":
# ImageMagick - capture root window # ImageMagick - capture root window
subprocess.run( result = subprocess.run(
["import", "-window", "root", str(screenshot_path)], ["import", "-window", "root", str(screenshot_path)],
check=True, check=True,
capture_output=True, capture_output=True,
timeout=config.SCREENSHOT_TIMEOUT timeout=config.SCREENSHOT_TIMEOUT,
env=env
) )
elif tool == "maim": elif tool == "maim":
# Capture active window # Capture full screen
subprocess.run( result = subprocess.run(
["maim", "-i", "$(xdotool getactivewindow)", str(screenshot_path)], ["maim", str(screenshot_path)],
shell=True,
check=True, check=True,
capture_output=True, capture_output=True,
timeout=config.SCREENSHOT_TIMEOUT timeout=config.SCREENSHOT_TIMEOUT,
env=env
) )
else: else:
return None return None
@ -86,17 +97,19 @@ def take_screenshot() -> Optional[str]:
else: else:
return None return None
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired as e:
if config.DEBUG: if config.DEBUG:
print(f"[DEBUG] Screenshot timeout with tool: {tool}") print(f"[DEBUG] Screenshot timeout with tool: {tool}")
return None return None
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if config.DEBUG: if config.DEBUG:
print(f"[DEBUG] Screenshot failed: {e}") 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 return None
except Exception as e: except Exception as e:
if config.DEBUG: if config.DEBUG:
print(f"[DEBUG] Unexpected error: {e}") print(f"[DEBUG] Unexpected screenshot error: {type(e).__name__}: {e}")
return None return None