| // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/base/win/mouse_wheel_util.h" |
| |
| #include "base/auto_reset.h" |
| #include "base/win/windowsx_shim.h" |
| #include "ui/base/view_prop.h" |
| #include "ui/gfx/win/hwnd_util.h" |
| |
| namespace ui { |
| |
| // Property used to indicate the HWND supports having mouse wheel messages |
| // rerouted to it. |
| static const char* const kHWNDSupportMouseWheelRerouting = |
| "__HWND_MW_REROUTE_OK"; |
| |
| static bool WindowSupportsRerouteMouseWheel(HWND window) { |
| while (GetWindowLong(window, GWL_STYLE) & WS_CHILD) { |
| if (!IsWindow(window)) |
| break; |
| |
| if (ViewProp::GetValue(window, kHWNDSupportMouseWheelRerouting) != NULL) { |
| return true; |
| } |
| window = GetParent(window); |
| } |
| return false; |
| } |
| |
| static bool IsCompatibleWithMouseWheelRedirection(HWND window) { |
| std::wstring class_name = gfx::GetClassName(window); |
| // Mousewheel redirection to comboboxes is a surprising and |
| // undesireable user behavior. |
| return !(class_name == L"ComboBox" || |
| class_name == L"ComboBoxEx32"); |
| } |
| |
| static bool CanRedirectMouseWheelFrom(HWND window) { |
| std::wstring class_name = gfx::GetClassName(window); |
| |
| // Older Thinkpad mouse wheel drivers create a window under mouse wheel |
| // pointer. Detect if we are dealing with this window. In this case we |
| // don't need to do anything as the Thinkpad mouse driver will send |
| // mouse wheel messages to the right window. |
| if ((class_name == L"Syn Visual Class") || |
| (class_name == L"SynTrackCursorWindowClass")) |
| return false; |
| |
| return true; |
| } |
| |
| ViewProp* SetWindowSupportsRerouteMouseWheel(HWND hwnd) { |
| return new ViewProp(hwnd, kHWNDSupportMouseWheelRerouting, |
| reinterpret_cast<HANDLE>(true)); |
| } |
| |
| bool RerouteMouseWheel(HWND window, WPARAM w_param, LPARAM l_param) { |
| // Since this is called from a subclass for every window, we can get |
| // here recursively. This will happen if, for example, a control |
| // reflects wheel scroll messages to its parent. Bail out if we got |
| // here recursively. |
| static bool recursion_break = false; |
| if (recursion_break) |
| return false; |
| // Check if this window's class has a bad interaction with rerouting. |
| if (!IsCompatibleWithMouseWheelRedirection(window)) |
| return false; |
| |
| DWORD current_process = GetCurrentProcessId(); |
| POINT wheel_location = { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) }; |
| HWND window_under_wheel = WindowFromPoint(wheel_location); |
| |
| if (!CanRedirectMouseWheelFrom(window_under_wheel)) |
| return false; |
| |
| // Find the lowest Chrome window in the hierarchy that can be the |
| // target of mouse wheel redirection. |
| while (window != window_under_wheel) { |
| // If window_under_wheel is not a valid Chrome window, then return true to |
| // suppress further processing of the message. |
| if (!::IsWindow(window_under_wheel)) |
| return true; |
| DWORD wheel_window_process = 0; |
| GetWindowThreadProcessId(window_under_wheel, &wheel_window_process); |
| if (current_process != wheel_window_process) { |
| if (IsChild(window, window_under_wheel)) { |
| // If this message is reflected from a child window in a different |
| // process (happens with out of process windowed plugins) then |
| // we don't want to reroute the wheel message. |
| return false; |
| } else { |
| // The wheel is scrolling over an unrelated window. Make sure that we |
| // have marked that window as supporting mouse wheel rerouting. |
| // Otherwise, we cannot send random WM_MOUSEWHEEL messages to arbitrary |
| // windows. So just drop the message. |
| if (!WindowSupportsRerouteMouseWheel(window_under_wheel)) |
| return true; |
| } |
| } |
| |
| // If the child window is transparent, then it is not interested in |
| // receiving wheel events. |
| if (IsChild(window, window_under_wheel) && |
| ::GetWindowLong( |
| window_under_wheel, GWL_EXSTYLE) & WS_EX_TRANSPARENT) { |
| return false; |
| } |
| |
| // window_under_wheel is a Chrome window. If allowed, redirect. |
| if (IsCompatibleWithMouseWheelRedirection(window_under_wheel)) { |
| base::AutoReset<bool> auto_reset_recursion_break(&recursion_break, true); |
| SendMessage(window_under_wheel, WM_MOUSEWHEEL, w_param, l_param); |
| return true; |
| } |
| // If redirection is disallowed, try the parent. |
| window_under_wheel = GetAncestor(window_under_wheel, GA_PARENT); |
| } |
| // If we traversed back to the starting point, we should process |
| // this message normally; return false. |
| return false; |
| } |
| |
| } // namespace ui |