87 lines
3.1 KiB
Python
87 lines
3.1 KiB
Python
import os
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
import time
|
||
from pathlib import Path
|
||
|
||
from .config import ScreenshotCfg
|
||
|
||
|
||
class ScreenshotCapture:
|
||
def __init__(self, cfg: ScreenshotCfg):
|
||
self._cfg = cfg
|
||
|
||
def capture(self) -> str:
|
||
"""截取全屏,返回保存路径。自动选择当前平台最佳方式。"""
|
||
if self._cfg.delay_ms > 0:
|
||
time.sleep(self._cfg.delay_ms / 1000)
|
||
|
||
out_path = self._cfg.tmp_path
|
||
Path(out_path).parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
if sys.platform == "darwin":
|
||
self._capture_macos(out_path)
|
||
else:
|
||
self._capture_linux(out_path)
|
||
|
||
if not Path(out_path).exists():
|
||
raise RuntimeError(f"截图文件未生成: {out_path}")
|
||
return out_path
|
||
|
||
# ------------------------------------------------------------------
|
||
# macOS
|
||
# ------------------------------------------------------------------
|
||
|
||
def _capture_macos(self, out_path: str) -> None:
|
||
# screencapture -x 静默截全屏(无声音/闪烁)
|
||
subprocess.run(["screencapture", "-x", out_path], check=True)
|
||
|
||
# ------------------------------------------------------------------
|
||
# Linux(按优先级尝试)
|
||
# ------------------------------------------------------------------
|
||
|
||
def _capture_linux(self, out_path: str) -> None:
|
||
wayland = os.environ.get("WAYLAND_DISPLAY")
|
||
|
||
if wayland:
|
||
# Wayland 优先:grim(轻量)> spectacle > gnome-screenshot
|
||
if shutil.which("grim"):
|
||
subprocess.run(["grim", out_path], check=True)
|
||
return
|
||
if shutil.which("spectacle"):
|
||
# spectacle -b 后台 -f 全屏 -n 无通知 -o 输出路径
|
||
# 丢弃 stderr(Tesseract 未安装的无害警告)
|
||
subprocess.run(
|
||
["spectacle", "-b", "-f", "-n", "-o", out_path],
|
||
check=True,
|
||
timeout=10,
|
||
stderr=subprocess.DEVNULL,
|
||
)
|
||
return
|
||
if shutil.which("gnome-screenshot"):
|
||
subprocess.run(["gnome-screenshot", "-f", out_path], check=True)
|
||
return
|
||
# 最后尝试 mss(可能在 XWayland 下有效)
|
||
self._capture_mss(out_path)
|
||
else:
|
||
# X11:mss 最快,失败则用 scrot
|
||
try:
|
||
self._capture_mss(out_path)
|
||
except Exception:
|
||
if shutil.which("scrot"):
|
||
subprocess.run(["scrot", out_path], check=True)
|
||
elif shutil.which("import"): # ImageMagick
|
||
subprocess.run(["import", "-window", "root", out_path], check=True)
|
||
else:
|
||
raise RuntimeError("未找到可用的截图工具(scrot / import)")
|
||
|
||
def _capture_mss(self, out_path: str) -> None:
|
||
import mss
|
||
import mss.tools
|
||
|
||
with mss.mss() as sct:
|
||
monitor = sct.monitors[0] # 0 = 全部显示器合并
|
||
screenshot = sct.grab(monitor)
|
||
mss.tools.to_png(screenshot.rgb, screenshot.size, output=out_path)
|