Conversation
- 新增 WheelFocusSwitcher:轮盘上屏前把前台焦点对齐到呼出点所在的顶层窗口, 复用 WindowTaskbarHelper.ActivateWindow 处理 Windows 前台锁,不注入点击、不改窗口样式、不移动窗口 - 只服务「鼠标呼出轮盘」这一条路径:键盘触发与插件经 IHostWheelService 呼出的粘滞轮盘一律不动前台 - 轮盘方案同步按呼出点所在窗口的进程名取,避免跨显示器时第一次呼出拿到另一块屏幕上那个程序的方案 - 进程名解析刻意只读(钩子线程不做激活),前台切换留在 UI 线程的 ShowRadialUI 里 - 判据保守:StarPie 自身进程窗口、WS_EX_NOACTIVATE / WS_EX_TRANSPARENT 悬浮层、最小化 / 不可见窗口、 桌面与任务栏等 Shell 表面都不是切换目标;命中 Shell 表面时不做 z 序下钻,宁可不动前台 - ActiveWindowHelper 增加 GetProcessNameForWindow(按任意窗口句柄取进程名), 缓存字段 _cachedForegroundHwnd 改名为 _cachedWindow 以反映其实际语义 - 新增 scratch/test_wheel_focus 端到端探针(17 项断言,含前置条件断言与 Shell 表面回归断言)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
需求描述
我在两个显示器跨显示器使用,如果鼠标焦点在显示器 1,我想在显示器 2 使用轮盘,但是焦点并不会自动切换到显示器 2,会对显示器 1 误操作,请求添加功能,能让轮盘让焦点自动切换到呼出轮盘的界面。
问题根因
轮盘触发键(右键 / 中键 / 侧键)的按下与抬起都被 StarPie 自己的低级鼠标钩子
MouseHook吞掉了,被点中的那个窗口收不到任何鼠标消息,Windows 自然不会把它切到前台 —— 前台仍留在按下之前那个窗口上。单屏上表现为「右键点在背景窗口上,动作却进了原来那个前台窗口」;多显示器上最明显:光标在显示器 2、轮盘也画在显示器 2,但松手后执行的动作(
SendInput注入的快捷键、窗口操作等)全部落进显示器 1 上原来那个前台窗口,形成误操作。修复方案
① 轮盘上屏之前把前台焦点对齐到呼出点所在的窗口
新增
WinPieGestures/WheelFocusSwitcher.cs,由GestureController.ShowRadialUI在轮盘真正Present之前调用一次SwitchFocusToPoint(呼出点)。必须放在
Present之前:轮盘一旦呈现,用户随时可能松手执行动作,而动作作用的对象就是此刻的前台窗口。激活复用既有的
WindowTaskbarHelper.ActivateWindow(前台锁的AttachThreadInput/Alt兜底都在那里)。本类只负责「解析出该切给谁」:不注入点击、不改窗口样式、不移动窗口;目标已经是前台时直接返回,不做任何多余的置顶 / 还原动作。② 只服务「鼠标呼出轮盘」这一条路径
ShowRadialUI新增switchFocusToWheelPoint参数,只有鼠标触发传true:_kbTriggerWaiting;isMouseWaiting。键盘触发(穿透模式)与插件经
IHostWheelService呼出的粘滞轮盘一律不动前台 —— 前者用户正在某个窗口里打字,后者连触发都不在钩子上,抢前台都是错的。③ 轮盘方案(扇区选项)必须一起跟过去
方案是按进程名取的(
ConfigManager.GetProfileForProcess),而进程名过去一律取ActiveWindowHelper.GetActiveWindowProcessName()(前台窗口)。跨显示器呼出时前台还在另一块屏幕上,于是第一次呼出拿到的是另一个程序的扇区配置 —— 前台虽然被切过去了,用户却得再呼出一次才看到该有的选项。所以鼠标路径改用
WheelFocusSwitcher.TryGetProcessNameAtPoint(呼出点):ProcessMove(初始位置)在BeginInvoke之前就按_activeProfile算好了初始扇区,松手执行的动作也读这一份。方案若等到 UI 线程才换,初判扇区会按另一个方案的扇区数算(12 扇区方案算出 10,套到 8 扇区方案上),用户松手可能落到错的扇区或空扇区。TryGetProcessNameAtPoint刻意只读(WindowFromPoint/GetAncestor/GetWindowRect/GetWindowLongPtr),前台切换仍留在ShowRadialUI(UI 线程)里。④ 解析判据(保守,宁可不切也不切错)
先
WindowFromPoint+GetAncestor(GA_ROOT);命中的是自己人的窗口(轮盘 HWND 隐藏后仍占着原位置、轨迹浮层)或 no-activate / 点击穿透的悬浮层时,沿 z 序自上而下兜底找真正压在下面的窗口。兜底只对上面这两类成立:命中的是 Shell 表面(桌面、任务栏)时绝不下钻 —— 桌面铺满整块屏幕、任务栏上方还压着最大化的程序,往下找到的第一个「矩形盖住该点」的窗口就是那个无关程序,在桌面 / 任务栏空白处呼出轮盘会把用户当前软件抢走;此时宁可不动前台。
被排除的目标:StarPie 自身进程的窗口、
WS_EX_NOACTIVATE/WS_EX_TRANSPARENT窗口、最小化 / 不可见窗口、桌面与任务栏等 Shell 表面。另有一处兜底:进程名取不出来(顶层的 UWP 进程经常
OpenProcess不到)时返回null,同样回落到前台窗口的原行为,避免把用户已配好的方案换成默认方案。变更文件
WinPieGestures/WheelFocusSwitcher.csWinPieGestures/ActiveWindowHelper.csGetProcessNameForWindow(窗口句柄);抽出共用ResolveProcessNameWinPieGestures/GestureController.csShowRadialUI增加焦点切换参数;两条鼠标路径改用呼出点进程名WinPieGestures/WinPieGestures.csprojInternalsVisibleToscratch/test_wheel_focus.cs/.csproj/.ps1scratch/Directory.Build.propsAGENTS.md/CHANGELOG.md验证
dotnet build WinPieGestures/WinPieGestures.csproj -c Release→ 0 警告 0 错误;scratch/test_wheel_focus.ps1端到端探针(真摆几层窗口)→ 17 项断言全部通过,连续 3 次运行稳定,EXIT=0,覆盖:WS_EX_NOACTIVATE置顶悬浮层被跳过、前台不会被切给它;--diag模式实测复现了「自己那层压在最上面」的关键处境(解析结果为0x0而非误取自身)。影响范围与兼容性
WheelFocusSwitcher为internal,ShowRadialUI为私有方法;ActiveWindowHelper只新增方法,既有方法签名不变。已知边界
ActivateWindow的 topmost 兜底仅在SetForegroundWindow失败时触发,白名单放行的独占全屏应用理论上可能被这条路径打扰一次。