Windows keeps the legacy path; Linux X11 uses a native XGrabKey hotkey (ctypes libX11) with native capture; Wayland captures through xdg-desktop-portal and degrades gracefully with explicit capability-unavailable logs when the portal/protocol is missing.
77 lines
3.8 KiB
Python
77 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""P1-04:桌面会话探测 + 截图/全局热键能力路由(纯 stdlib,可在任何平台导入)。
|
||
|
||
契约(REPAIR_BACKLOG P1-04):
|
||
- Windows 保持现有行为(Win32 RegisterHotKey + Qt grabWindow 覆盖层)。
|
||
- Linux X11:原生全局快捷键(XGrabKey,见 x11_hotkey.py)+ 原生屏幕捕获(Qt grabWindow,X11 可用)。
|
||
- Linux Wayland:截图走 xdg-desktop-portal(用户授权,不绕过 compositor,见 portal_capture.py);
|
||
全局热键依赖 compositor 桌面协议(ext-global-shortcut 等),本版本无免依赖实现 →
|
||
明确告知不可用,仅保留应用内 Alt+S 快捷键,主程序其余功能不受影响。
|
||
- offscreen/无显示(unknown):能力不可用要有明确日志,主程序仍可聊天。
|
||
"""
|
||
import os
|
||
import sys
|
||
|
||
|
||
def session_kind() -> str:
|
||
"""返回 "win32" / "x11" / "wayland" / "unknown"。
|
||
|
||
判定顺序(Linux,综合 Qt 平台名与 XDG_SESSION_TYPE,见 PLATFORM_PLAN):
|
||
1. QT_QPA_PLATFORM 以 offscreen 开头 → unknown(自动化离屏,无桌面能力)
|
||
2. QT_QPA_PLATFORM 以 wayland 开头,或 WAYLAND_DISPLAY 已设置,
|
||
或 XDG_SESSION_TYPE=wayland → wayland
|
||
3. DISPLAY 已设置,或 XDG_SESSION_TYPE=x11 → x11
|
||
4. 其余(headless/无显示)→ unknown
|
||
"""
|
||
if sys.platform == "win32":
|
||
return "win32"
|
||
if sys.platform != "linux":
|
||
# 其他 POSIX(macOS 等)不在本任务支持矩阵内
|
||
return "unknown"
|
||
qt_plat = os.environ.get("QT_QPA_PLATFORM", "").strip()
|
||
if qt_plat.startswith("offscreen"):
|
||
return "unknown"
|
||
xdg_type = os.environ.get("XDG_SESSION_TYPE", "").strip().lower()
|
||
if (qt_plat.startswith("wayland")
|
||
or os.environ.get("WAYLAND_DISPLAY", "").strip()
|
||
or xdg_type == "wayland"):
|
||
return "wayland"
|
||
if os.environ.get("DISPLAY", "").strip() or xdg_type == "x11":
|
||
return "x11"
|
||
return "unknown"
|
||
|
||
|
||
def hotkey_plan(kind: str):
|
||
"""全局热键能力路由 → (thread_factory | None, message)。
|
||
|
||
thread_factory() 返回 QThread(带 triggered 信号);None 表示无全局热键能力
|
||
(调用方应保留应用内 QShortcut 兜底)。message 需要打印以明确当前能力。
|
||
"""
|
||
if kind == "win32":
|
||
from ui.views.system_tools.global_hotkey import GlobalHotkeyThread
|
||
return GlobalHotkeyThread, "[GlobalHotkey] Windows:系统级全局热键 Alt+S(RegisterHotKey)"
|
||
if kind == "x11":
|
||
from ui.views.system_tools.x11_hotkey import X11HotkeyThread
|
||
return X11HotkeyThread, "[GlobalHotkey] X11:原生全局热键 Alt+S(XGrabKey)"
|
||
if kind == "wayland":
|
||
return None, ("[GlobalHotkey] Wayland:全局热键需要 compositor 桌面协议"
|
||
"(ext-global-shortcut 等),本版本未启用 → 仅提供应用内 Alt+S 快捷键"
|
||
"(窗口获焦时生效)与截图按钮;其余功能不受影响")
|
||
return None, ("[GlobalHotkey] 当前环境无显示服务(offscreen/无 DISPLAY)→ 全局热键不可用;"
|
||
"应用内 Alt+S 快捷键同样不可用,可用截图按钮以外的全部功能")
|
||
|
||
|
||
def capture_plan(kind: str):
|
||
"""截图能力路由 → (mode, message)。
|
||
|
||
mode: "overlay"(Qt grabWindow 覆盖层,win32/x11)
|
||
"portal"(xdg-desktop-portal 交互截图,wayland)
|
||
"unavailable"(unknown:明确告知,主程序其余功能不受影响)
|
||
"""
|
||
if kind in ("win32", "x11"):
|
||
return "overlay", None
|
||
if kind == "wayland":
|
||
return "portal", None
|
||
return "unavailable", ("[Screenshot] 当前环境无法获取屏幕画面(offscreen/无显示)→ "
|
||
"截图功能不可用;聊天与其他功能不受影响")
|