Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions source/MaaWin32ControlUnit/Input/InputUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ inline void ensure_foreground_and_topmost(HWND hwnd)

// 如果窗口不在前台,先将其置顶
if (hwnd != GetForegroundWindow()) {
// 检查当前前台窗口是否是 hwnd 拥有的弹出窗口(如下拉菜单、右键菜单等)。
// 若是,则跳过置前操作:强制置前 hwnd 会令弹出窗口失去焦点并自动关闭,
// 导致后续点击落在错误的 UI 元素上。
HWND fg = GetForegroundWindow();
if (fg) {
HWND owner = GetWindow(fg, GW_OWNER);
while (owner) {
if (owner == hwnd) {
return;
}
owner = GetWindow(owner, GW_OWNER);
}
}

// 将窗口移到 Z 序顶部
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
Expand Down
52 changes: 51 additions & 1 deletion source/MaaWin32ControlUnit/Input/MessageInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,39 @@ HWND MessageInput::get_active_hwnd()
return hwnd_;
}

HWND MessageInput::find_window_at_point(int x, int y) const
{
if (!hwnd_) {
return nullptr;
}

POINT screen_pt = { x, y };
if (!ClientToScreen(hwnd_, &screen_pt)) {
return nullptr;
}

HWND at_pos = WindowFromPoint(screen_pt);
if (!at_pos || at_pos == hwnd_) {
return nullptr;
}
if (!IsWindowVisible(at_pos)) {
return nullptr;
}

// 仅对同进程的窗口(如同进程内的弹出菜单或子控件)启用此逻辑,
// 避免误点击属于无关进程的窗口。
DWORD our_pid = 0, at_pid = 0;
if (!GetWindowThreadProcessId(hwnd_, &our_pid) || !GetWindowThreadProcessId(at_pos, &at_pid)) {
LogWarn << "GetWindowThreadProcessId failed in find_window_at_point" << VAR(GetLastError());
return nullptr;
}
if (our_pid != at_pid) {
return nullptr;
}

return at_pos;
}

LPARAM MessageInput::make_mouse_lparam(HWND target, int x, int y)
{
if (target == hwnd_) {
Expand Down Expand Up @@ -748,7 +781,24 @@ bool MessageInput::touch_down(int contact, int x, int y, int pressure)
return false;
}

HWND target = send_activate();
// 优先使用 WindowFromPoint 查找点击位置处实际存在的窗口(如覆盖在主窗口上方的弹出菜单)。
// GetLastActivePopup 仅能感知所有者链中的弹出窗口,无法发现不在所有者链中的同进程覆盖窗口,
// 导致后台消息直接发送给主窗口,绕过了上层的弹出窗口。
HWND window_at_pos = find_window_at_point(x, y);
HWND target;
if (window_at_pos) {
target = window_at_pos;
LogInfo << "Using window at point instead of active HWND" << VAR(target);
// 若目标窗口已处于前台(如刚打开的下拉菜单),则不再发送 WM_ACTIVATE,
// 避免激活消息导致弹出窗口意外关闭。
if (target != GetForegroundWindow()) {
bool use_post = (config_.mode == Mode::PostMessage);
::MaaNS::CtrlUnitNs::send_activate_message(target, use_post);
}
}
else {
target = send_activate();
}
gesture_target_ = target;

check_and_block_input();
Expand Down
1 change: 1 addition & 0 deletions source/MaaWin32ControlUnit/Input/MessageInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class MessageInput : public RelativeMoveInput
bool send_or_post_w(HWND target, UINT message, WPARAM wParam, LPARAM lParam);

HWND get_active_hwnd();
HWND find_window_at_point(int x, int y) const;
LPARAM make_mouse_lparam(HWND target, int x, int y);

// 在发鼠标消息前把系统状态调整到目标窗口愿意接受的位置。
Expand Down