1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/base/win/mouse_wheel_util.h"
9 #include "base/auto_reset.h"
10 #include "ui/base/view_prop.h"
11 #include "ui/gfx/win/hwnd_util.h"
15 // Property used to indicate the HWND supports having mouse wheel messages
17 static const char* const kHWNDSupportMouseWheelRerouting
=
18 "__HWND_MW_REROUTE_OK";
20 static bool WindowSupportsRerouteMouseWheel(HWND window
) {
21 while (GetWindowLong(window
, GWL_STYLE
) & WS_CHILD
) {
22 if (!IsWindow(window
))
25 if (ViewProp::GetValue(window
, kHWNDSupportMouseWheelRerouting
) != NULL
) {
28 window
= GetParent(window
);
33 static bool IsCompatibleWithMouseWheelRedirection(HWND window
) {
34 std::wstring class_name
= gfx::GetClassName(window
);
35 // Mousewheel redirection to comboboxes is a surprising and
36 // undesireable user behavior.
37 return !(class_name
== L
"ComboBox" ||
38 class_name
== L
"ComboBoxEx32");
41 static bool CanRedirectMouseWheelFrom(HWND window
) {
42 std::wstring class_name
= gfx::GetClassName(window
);
44 // Older Thinkpad mouse wheel drivers create a window under mouse wheel
45 // pointer. Detect if we are dealing with this window. In this case we
46 // don't need to do anything as the Thinkpad mouse driver will send
47 // mouse wheel messages to the right window.
48 if ((class_name
== L
"Syn Visual Class") ||
49 (class_name
== L
"SynTrackCursorWindowClass"))
55 ViewProp
* SetWindowSupportsRerouteMouseWheel(HWND hwnd
) {
56 return new ViewProp(hwnd
, kHWNDSupportMouseWheelRerouting
,
57 reinterpret_cast<HANDLE
>(true));
60 bool RerouteMouseWheel(HWND window
, WPARAM w_param
, LPARAM l_param
) {
61 // Since this is called from a subclass for every window, we can get
62 // here recursively. This will happen if, for example, a control
63 // reflects wheel scroll messages to its parent. Bail out if we got
65 static bool recursion_break
= false;
68 // Check if this window's class has a bad interaction with rerouting.
69 if (!IsCompatibleWithMouseWheelRedirection(window
))
72 DWORD current_process
= GetCurrentProcessId();
73 POINT wheel_location
= { GET_X_LPARAM(l_param
), GET_Y_LPARAM(l_param
) };
74 HWND window_under_wheel
= WindowFromPoint(wheel_location
);
76 if (!CanRedirectMouseWheelFrom(window_under_wheel
))
79 // Find the lowest Chrome window in the hierarchy that can be the
80 // target of mouse wheel redirection.
81 while (window
!= window_under_wheel
) {
82 // If window_under_wheel is not a valid Chrome window, then return true to
83 // suppress further processing of the message.
84 if (!::IsWindow(window_under_wheel
))
86 DWORD wheel_window_process
= 0;
87 GetWindowThreadProcessId(window_under_wheel
, &wheel_window_process
);
88 if (current_process
!= wheel_window_process
) {
89 if (IsChild(window
, window_under_wheel
)) {
90 // If this message is reflected from a child window in a different
91 // process (happens with out of process windowed plugins) then
92 // we don't want to reroute the wheel message.
95 // The wheel is scrolling over an unrelated window. Make sure that we
96 // have marked that window as supporting mouse wheel rerouting.
97 // Otherwise, we cannot send random WM_MOUSEWHEEL messages to arbitrary
98 // windows. So just drop the message.
99 if (!WindowSupportsRerouteMouseWheel(window_under_wheel
))
104 // If the child window is transparent, then it is not interested in
105 // receiving wheel events.
106 if (IsChild(window
, window_under_wheel
) &&
108 window_under_wheel
, GWL_EXSTYLE
) & WS_EX_TRANSPARENT
) {
112 // window_under_wheel is a Chrome window. If allowed, redirect.
113 if (IsCompatibleWithMouseWheelRedirection(window_under_wheel
)) {
114 base::AutoReset
<bool> auto_reset_recursion_break(&recursion_break
, true);
115 SendMessage(window_under_wheel
, WM_MOUSEWHEEL
, w_param
, l_param
);
118 // If redirection is disallowed, try the parent.
119 window_under_wheel
= GetAncestor(window_under_wheel
, GA_PARENT
);
121 // If we traversed back to the starting point, we should process
122 // this message normally; return false.