diff --git a/electron/main.ts b/electron/main.ts index a60c898..3a5dab3 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -1043,6 +1043,13 @@ function registerIpcHandlers() { return app.getVersion() }) + ipcMain.handle('app:checkWayland', async () => { + if (process.platform !== 'linux') return false; + + const sessionType = process.env.XDG_SESSION_TYPE?.toLowerCase(); + return Boolean(process.env.WAYLAND_DISPLAY || sessionType === 'wayland'); + }) + ipcMain.handle('log:getPath', async () => { return join(app.getPath('userData'), 'logs', 'wcdb.log') }) diff --git a/electron/preload.ts b/electron/preload.ts index f12a272..b1c9c30 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -63,7 +63,8 @@ contextBridge.exposeInMainWorld('electronAPI', { onUpdateAvailable: (callback: (info: { version: string; releaseNotes: string }) => void) => { ipcRenderer.on('app:updateAvailable', (_, info) => callback(info)) return () => ipcRenderer.removeAllListeners('app:updateAvailable') - } + }, + checkWayland: () => ipcRenderer.invoke('app:checkWayland'), }, // 日志 diff --git a/electron/services/keyServiceLinux.ts b/electron/services/keyServiceLinux.ts index 8fe2cc4..7364f83 100644 --- a/electron/services/keyServiceLinux.ts +++ b/electron/services/keyServiceLinux.ts @@ -1,10 +1,9 @@ import { app } from 'electron' import { join } from 'path' import { existsSync, readdirSync, statSync, readFileSync } from 'fs' -import { execFile, exec } from 'child_process' +import { execFile, exec, spawn } from 'child_process' import { promisify } from 'util' import { createRequire } from 'module'; -import { spawn } from 'child_process' const require = createRequire(import.meta.url); const execFileAsync = promisify(execFile) diff --git a/src/App.tsx b/src/App.tsx index eb8cd5c..e09f7ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -104,6 +104,44 @@ function App() { // 数据收集同意状态 const [showAnalyticsConsent, setShowAnalyticsConsent] = useState(false) + const [showWaylandWarning, setShowWaylandWarning] = useState(false) + + useEffect(() => { + const checkWaylandStatus = async () => { + try { + // 防止在非客户端环境报错,先检查 API 是否存在 + if (!window.electronAPI?.app?.checkWayland) return + + // 通过 configService 检查是否已经弹过窗 + const hasWarned = await window.electronAPI.config.get('waylandWarningShown') + + if (!hasWarned) { + const isWayland = await window.electronAPI.app.checkWayland() + if (isWayland) { + setShowWaylandWarning(true) + } + } + } catch (e) { + console.error('检查 Wayland 状态失败:', e) + } + } + + // 只有在协议同意之后并且已经进入主应用流程才检查 + if (!isAgreementWindow && !isOnboardingWindow && !agreementLoading) { + checkWaylandStatus() + } + }, [isAgreementWindow, isOnboardingWindow, agreementLoading]) + + const handleDismissWaylandWarning = async () => { + try { + // 记录到本地配置中,下次不再提示 + await window.electronAPI.config.set('waylandWarningShown', true) + } catch (e) { + console.error('保存 Wayland 提示状态失败:', e) + } + setShowWaylandWarning(false) + } + useEffect(() => { if (location.pathname !== '/settings') { settingsBackgroundRef.current = location @@ -432,6 +470,8 @@ function App() { checkLock() }, [isAgreementWindow, isOnboardingWindow, isVideoPlayerWindow]) + + // 独立协议窗口 if (isAgreementWindow) { return @@ -614,6 +654,33 @@ function App() { )} + {showWaylandWarning && ( +
+
+
+ +

环境兼容性提示 (Wayland)

+
+
+
+

检测到您当前正在使用 Wayland 显示服务器。

+

在 Wayland 环境下,出于系统级的安全与设计机制,应用程序无法直接控制新弹出窗口的位置

+

这可能导致某些独立窗口(如消息通知、图片查看器等)出现位置随机、或不受控制的情况。这是底层机制导致的,对此我们无能为力。

+
+

如果您觉得窗口位置异常严重影响了使用体验,建议尝试:

+

1. 在系统登录界面,将会话切换回 X11 (Xorg) 模式。

+

2. 修改您的桌面管理器 (WM/DE) 配置,强制指定该应用程序的窗口规则。

+
+
+
+
+ +
+
+
+
+ )} + {/* 更新提示对话框 */} Promise<{ success: boolean }> onDownloadProgress: (callback: (progress: number) => void) => () => void onUpdateAvailable: (callback: (info: { version: string; releaseNotes: string }) => void) => () => void + checkWayland: () => Promise } notification: { show: (data: { title: string; content: string; avatarUrl?: string; sessionId: string }) => Promise<{ success?: boolean; error?: string } | void>