shanselman--peekdesktop
229 行
12 KiB
Markdown
229 行
12 KiB
Markdown
<!-- WEHUB_ZH_README -->
|
||
> [!NOTE]
|
||
> 本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。
|
||
> [English](./README.en.md) · [原始项目](https://github.com/shanselman/PeekDesktop) · [上游 README](https://github.com/shanselman/PeekDesktop/blob/HEAD/README.md)
|
||
> 原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。
|
||
|
||
# PeekDesktop 👀
|
||
|
||
**点击空白桌面壁纸(或空白任务栏区域)即可显示桌面 — 就像 macOS Sonoma 一样。**
|
||
|
||
PeekDesktop 将 macOS Sonoma 的「点击壁纸显示桌面」功能带到了 Windows 10 和 11。默认情况下,它使用资源管理器(Explorer)原生的 **Show Desktop** 行为,同时还包含可选的 **Fly Away** 实验性样式,以及托盘开关:**Require Double-Click**、**Peek on Desktop Click** 和 **Peek on Taskbar Click**。你可以正常点击或拖动桌面图标,而不会意外触发 peek。完成后,点击任意窗口、任务栏或壁纸,一切就会回到原来的位置。
|
||
|
||
<p align="center">
|
||
<img src="img/demo.gif" alt="PeekDesktop 演示:点击壁纸时窗口最小化" width="900" />
|
||
</p>
|
||
|
||
## 下载
|
||
|
||
📥 **[下载最新版本](https://github.com/shanselman/PeekDesktop/releases/latest)**
|
||
|
||
### 使用 Winget 安装
|
||
|
||
PeekDesktop 在发布带标签的版本时,会以 `Hanselman.PeekDesktop` 的形式提交到 Windows Package Manager 社区仓库。社区清单被接受后,可使用以下命令安装:
|
||
|
||
```powershell
|
||
winget install Hanselman.PeekDesktop
|
||
```
|
||
|
||
### 下载发行版
|
||
|
||
| File | Platform |
|
||
|------|----------|
|
||
| `PeekDesktop-vX.Y.Z-win-x64.zip` | Intel/AMD(大多数 PC) |
|
||
| `PeekDesktop-vX.Y.Z-win-arm64.zip` | ARM64(Surface Pro X、骁龙等) |
|
||
|
||
无需安装程序。下载 zip 文件,解压后运行 `PeekDesktop.exe`。发行版构建为 **self-contained(自包含)**,因此无需单独安装 .NET。它驻留在系统托盘中,并会 **自动更新** — 有新版本可用时,会下载、验证代码签名,并原地重启。
|
||
|
||
## 文档
|
||
|
||
- **[工程深入剖析](Docs/PeekDesktop-Engineering-Deep-Dive.md)** - 架构、Shell 内部机制、实验、调试工作流、未公开 API 说明及发行版权衡
|
||
- **[自动更新器](Docs/Auto-Updater.md)** - 原地自动更新系统的工作原理、安全模型、交换流程与测试
|
||
|
||
## 工作原理
|
||
|
||
1. **点击空白桌面壁纸或空白任务栏区域**(不是图标或任务栏按钮)-> 显示你的桌面
|
||
2. **停留在桌面上** -> 点击或拖动图标、右键点击并重新排列,同时窗口保持隐藏
|
||
3. **再次点击任意应用、任务栏或空白壁纸** -> 所有窗口恢复到原来的确切位置
|
||
|
||
就这么简单。开箱即用。
|
||
|
||
## Peek 样式
|
||
|
||
- **Show Desktop (Explorer)** — 默认且推荐的模式。使用 Explorer 原生的 Show Desktop 行为。
|
||
- **Fly Away (Experimental)** — 将窗口动画移出屏幕。有趣,但在与外部窗口管理(Win+D、任务栏)配合时存在已知怪癖。适合追求视觉效果,但若 Shell 在后台改变了窗口状态,可能会出现混乱。
|
||
|
||
### 底层原理
|
||
|
||
PeekDesktop 使用轻量级 Windows API:
|
||
|
||
- **`SetWindowsHookEx(WH_MOUSE_LL)`** — 底层鼠标钩子,用于检测桌面点击
|
||
- **`WindowFromPoint`** — 识别光标下方的窗口
|
||
- **MSAA hit-testing (`AccessibleObjectFromPoint`)** — 区分空白壁纸与桌面图标
|
||
- **UI Automation hit-testing** — 分类空白任务栏空间,不会在「开始」、固定应用或托盘按钮上触发
|
||
- **Taskbar Show Desktop button click** — 主要路径,不受键盘重映射影响(PowerToys 等)
|
||
- **Win+D `SendInput`** — 任务栏按钮不可用时的备用方案
|
||
- **`EnumWindows` + `WINDOWPLACEMENT`** — 捕获每个窗口的精确位置和状态(包括最大化)
|
||
- **`SetWinEventHook(EVENT_SYSTEM_FOREGROUND)`** — 监视你切换回应用的时刻
|
||
- **`SetWindowPlacement`** — 将窗口恢复到之前的精确位置
|
||
|
||
无需管理员权限。空闲时占用 < 5 MB 内存。
|
||
|
||
## 系统托盘
|
||
|
||
右键点击托盘图标可查看选项:
|
||
|
||
- ✅ **Enabled** — 开启/关闭 peek 功能
|
||
- 🔁 **Start with Windows** — 登录时自动启动
|
||
- 🖱️ **Require Double-Click** — 可选择在桌面上双击以触发 peek
|
||
- 🎮 **Pause While Gaming / Full-Screen** — 默认开启,适用于独占全屏和已知的游戏全屏应用
|
||
- 🖥️ **Peek on Desktop Click** — 默认开启;关闭后仅通过任务栏激活
|
||
- 📌 **Peek on Taskbar Click** — 可选择在空白任务栏空间触发 peek
|
||
- 🪟 **Restore All Windows on App Switch** — 默认开启;在 Explorer show desktop 模式下,通过任务栏/Alt+Tab 切换应用时,会在所选应用背后恢复所有隐藏窗口
|
||
- 👀 **Peek Style** — 在 Explorer 与 fly-away 模式之间切换
|
||
- ℹ️ **About** — 版本信息
|
||
- ⬇️ **Check for Updates** — 自动下载并安装较新版本
|
||
- 🔄 **Auto-Check for Updates** — 默认开启;启动时静默检查更新
|
||
- ❌ **Exit** — 退出 PeekDesktop
|
||
|
||
当 Windows 处于深色模式时,托盘菜单也会在操作系统支持的情况下跟随系统主题。
|
||
|
||
## 新特性
|
||
|
||
- **小型 Native AOT 单文件构建**,同时支持 x64 和 ARM64
|
||
- **Peek on Taskbar Click** — 可选择在空白任务栏空间触发
|
||
- **仅任务栏激活** — 禁用桌面点击 peek,同时保持任务栏 peek 启用
|
||
- **深色托盘菜单支持** — 在可用时跟随 Windows 深色模式
|
||
- **Taskbar button Show Desktop** — 绕过键盘重映射器(PowerToys Keyboard Manager 等)
|
||
- **Pause While Gaming / Full-Screen** — 在游戏过程中避免干扰
|
||
- **Require Double-Click** — 桌面 peek 的可选双击触发
|
||
- **原地自动更新器** — 自动下载、验证 Authenticode 签名、交换并重启
|
||
|
||
## macOS Sonoma 与 PeekDesktop 对比
|
||
|
||
| Feature | macOS Sonoma | PeekDesktop |
|
||
|---------|-------------|-------------|
|
||
| 点击壁纸以 peek | ✅ | ✅ |
|
||
| 点击应用后恢复 | ✅ | ✅ |
|
||
| 再次点击壁纸后恢复 | ✅ | ✅ |
|
||
| 点击/拖动图标不会触发 peek | ✅ | ✅ |
|
||
| 可访问桌面图标 | ✅ | ✅ |
|
||
| 精确恢复窗口位置 | ✅ | ✅ |
|
||
| 系统托盘控制 | ❌ | ✅ |
|
||
| 多显示器支持 | ✅ | ✅ |
|
||
| 随系统启动 | Login Items | ✅ Registry |
|
||
| 平滑动画 | ✅ | Fly Away 模式 |
|
||
|
||
## 从源码构建
|
||
|
||
**Requirements:** [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
|
||
|
||
```bash
|
||
git clone https://github.com/shanselman/PeekDesktop.git
|
||
cd PeekDesktop
|
||
dotnet build src/PeekDesktop/PeekDesktop.csproj
|
||
```
|
||
|
||
### 运行
|
||
|
||
```bash
|
||
dotnet run --project src/PeekDesktop/PeekDesktop.csproj
|
||
|
||
# Run the P/Invoke safety harness (invalid handles + stress/leak checks)
|
||
dotnet run --project src/PeekDesktop.InteropHarness/PeekDesktop.InteropHarness.csproj -- 10000
|
||
|
||
# Windows-friendly wrapper script
|
||
.\test.ps1 -Iterations 10000
|
||
|
||
# Verbose mode (prints per-test timing + leak probe diagnostics)
|
||
.\test.ps1 -Iterations 10000 -VerboseOutput
|
||
```
|
||
|
||
### 发布自包含单文件 exe
|
||
|
||
```bash
|
||
# For Intel/AMD
|
||
dotnet publish src/PeekDesktop/PeekDesktop.csproj -c Release -r win-x64 --self-contained -p:PublishSingleFile=true
|
||
|
||
# For ARM64
|
||
dotnet publish src/PeekDesktop/PeekDesktop.csproj -c Release -r win-arm64 --self-contained -p:PublishSingleFile=true
|
||
```
|
||
|
||
### 发行版打包
|
||
|
||
发行版构建使用 **.NET Native AOT** — exe 是完全原生二进制文件,不依赖 .NET 运行时。当前发行版以自包含单文件可执行文件形式发布,同时支持 x64 和 ARM64。早期实验也曾使用 [PublishAotCompressed](https://github.com/MichalStrehovsky/PublishAotCompressed) (LZMA),但当前构建更注重兼容性和可预测的启动行为。
|
||
|
||
## 架构
|
||
|
||
```
|
||
src/PeekDesktop/
|
||
├── Program.cs # Entry point, single-instance mutex
|
||
├── DesktopPeek.cs # Core state machine (Idle ↔ Peeking)
|
||
├── MouseHook.cs # WH_MOUSE_LL global mouse hook
|
||
├── FocusWatcher.cs # EVENT_SYSTEM_FOREGROUND monitor
|
||
├── WindowTracker.cs # Enumerate, minimize, and restore windows
|
||
├── DesktopDetector.cs # Identify desktop windows, icons, taskbar
|
||
├── Win32MessageLoop.cs # Win32 message loop + TaskbarCreated recovery
|
||
├── Win32TrayIcon.cs # Shell_NotifyIcon wrapper
|
||
├── Win32Menu.cs # Win32 popup menu wrapper
|
||
├── Win32Icon.cs # Programmatic icon via CreateIconIndirect
|
||
├── WinHttp.cs # WinHTTP wrapper (replaces HttpClient)
|
||
├── TrayIcon.cs # Tray icon business logic + menu wiring
|
||
├── AppUpdater.cs # In-place auto-updater (download, verify, swap, restart)
|
||
├── AppDiagnostics.cs # Logging via Trace/DebugView
|
||
├── Settings.cs # Hand-written UTF-8 JSON persistence + autostart
|
||
└── NativeMethods.cs # Win32 P/Invoke declarations
|
||
```
|
||
|
||
## 贡献
|
||
|
||
欢迎 PR!当前状态与后续计划:
|
||
|
||
- [x] 点击空白壁纸以 peek
|
||
- [x] 点击空白任务栏区域以 peek(可选开启)
|
||
- [x] 点击应用或任务栏后恢复
|
||
- [x] 再次点击壁纸后恢复
|
||
- [x] 点击或拖动桌面图标**不会**触发 peek
|
||
- [x] peek 时右键桌面图标(上下文菜单保持打开)
|
||
- [x] peek 时桌面图标仍可使用
|
||
- [x] 精确恢复窗口位置
|
||
- [x] 基于 GitHub release 的更新检查
|
||
- [x] 可与 PowerToys Keyboard Manager 配合使用(键盘重映射)
|
||
- [ ] 流畅的最小化/恢复动画(滑动/淡入淡出)
|
||
- [ ] 快捷键支持(例如,用 `Ctrl+F12` 切换 peek)
|
||
- [ ] 按显示器 peek(仅最小化被点击显示器上的窗口)
|
||
- [ ] 排除特定应用不被最小化
|
||
|
||
## .NET Native AOT — 体积优化之路 💾
|
||
|
||
PeekDesktop 展示了 .NET Native AOT 应用可以做得多小。从一个标准的 WinForms 应用出发,我们系统性地移除了每一处托管框架依赖,直到二进制文件变为纯 Win32 P/Invoke —— 再压缩到能放进一张软盘。
|
||
|
||
| 版本 | 二进制体积 | 变更内容 |
|
||
|---------|------------|--------------|
|
||
| v0.4.5 | ~65 MB | 自包含 .NET(无 AOT) |
|
||
| v0.5.0 | 17.5 MB | 启用 Native AOT |
|
||
| v0.6.0 | 4.2 MB | 移除 WinForms —— 托盘图标、菜单、消息循环改用纯 Win32 P/Invoke |
|
||
| v0.6.1 | 2.3 MB | 用操作系统原生 WinHTTP(`winhttp.dll`)替换 `HttpClient` |
|
||
| v0.7.2 | 1.88 MB | 移除 JSON source generator、`System.Reflection`、`Process.Start` |
|
||
| v0.7.2 + LZMA | **~564 KB** | **通过 [PublishAotCompressed](https://github.com/MichalStrehovsky/PublishAotCompressed)** 进行 LZMA 压缩 |
|
||
|
||
**压缩前 1.88 MB 里还剩什么?**
|
||
- ~1.2 MB — .NET Native AOT runtime(GC、线程、异常处理、类型系统)
|
||
- ~0.4 MB — `Utf8JsonReader`/`Utf8JsonWriter` + async task machinery
|
||
- ~0.2 MB — 应用代码、P/Invoke stubs、字符串字面量
|
||
- ~0.08 MB — PE headers 与 metadata
|
||
|
||
**关键技术:**
|
||
- **无 WinForms、无 System.Drawing** — 通过 P/Invoke 使用 `Shell_NotifyIcon`、`CreatePopupMenu`、`TrackPopupMenuEx`、`MessageBoxW`、`CreateIconIndirect`
|
||
- **无 HttpClient** — `WinHttpOpen`/`WinHttpSendRequest` 以零二进制成本使用操作系统 HTTP+TLS 栈
|
||
- **无 JSON source generator** — 为所需的两个极小 JSON 结构手写 `Utf8JsonReader`/`Utf8JsonWriter`
|
||
- **无 System.Reflection** — 通过 `GetFileVersionInfoExW` P/Invoke 读取 PE version resources
|
||
- **WndProc 不使用托管委托** — `UnmanagedCallersOnly` 函数指针避免 marshaling 开销
|
||
- **`OptimizationPreference=Size`** + `InvariantGlobalization` + 精简 diagnostics
|
||
|
||
特别感谢 [Michal Strehovský](https://github.com/MichalStrehovsky) —— .NET Native AOT 的架构师 —— 他的 [PR #5](https://github.com/shanselman/PeekDesktop/pull/5) 启发了最后一轮优化,移除了 JSON source generator、reflection 与托管委托。当*构建* AOT 编译器的人亲自优化你的应用时,你会格外重视。🙏
|
||
|
||
## 许可证
|
||
|
||
[MIT](LICENSE)
|