1 // Copyright 2014 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 "chrome/browser/ui/views/tabs/window_finder.h"
7 #include "base/win/scoped_gdi_object.h"
8 #include "base/win/windows_version.h"
9 #include "ui/aura/window.h"
10 #include "ui/gfx/screen.h"
11 #include "ui/gfx/win/dpi.h"
12 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
13 #include "ui/views/win/hwnd_util.h"
16 gfx::NativeWindow
GetLocalProcessWindowAtPointAsh(
17 const gfx::Point
& screen_point
,
18 const std::set
<gfx::NativeWindow
>& ignore
);
23 // BaseWindowFinder -----------------------------------------------------------
25 // Base class used to locate a window. This is intended to be used with the
26 // various win32 functions that iterate over windows.
28 // A subclass need only override ShouldStopIterating to determine when
29 // iteration should stop.
30 class BaseWindowFinder
{
32 // Creates a BaseWindowFinder with the specified set of HWNDs to ignore.
33 explicit BaseWindowFinder(const std::set
<HWND
>& ignore
) : ignore_(ignore
) {}
34 virtual ~BaseWindowFinder() {}
37 static BOOL CALLBACK
WindowCallbackProc(HWND hwnd
, LPARAM lParam
) {
38 // Cast must match that in as_lparam().
39 BaseWindowFinder
* finder
= reinterpret_cast<BaseWindowFinder
*>(lParam
);
40 if (finder
->ignore_
.find(hwnd
) != finder
->ignore_
.end())
43 return finder
->ShouldStopIterating(hwnd
) ? FALSE
: TRUE
;
47 // Cast must match that in WindowCallbackProc().
48 return reinterpret_cast<LPARAM
>(static_cast<BaseWindowFinder
*>(this));
51 // Returns true if iteration should stop, false if iteration should continue.
52 virtual bool ShouldStopIterating(HWND window
) = 0;
55 const std::set
<HWND
>& ignore_
;
57 DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder
);
60 // TopMostFinder --------------------------------------------------------------
62 // Helper class to determine if a particular point of a window is not obscured
64 class TopMostFinder
: public BaseWindowFinder
{
66 // Returns true if |window| is the topmost window at the location
67 // |screen_loc|, not including the windows in |ignore|.
68 static bool IsTopMostWindowAtPoint(HWND window
,
69 const gfx::Point
& screen_loc
,
70 const std::set
<HWND
>& ignore
) {
71 TopMostFinder
finder(window
, screen_loc
, ignore
);
72 return finder
.is_top_most_
;
75 virtual bool ShouldStopIterating(HWND hwnd
) {
76 if (hwnd
== target_
) {
77 // Window is topmost, stop iterating.
82 if (!IsWindowVisible(hwnd
)) {
83 // The window isn't visible, keep iterating.
88 if (!GetWindowRect(hwnd
, &r
) || !PtInRect(&r
, screen_loc_
.ToPOINT())) {
89 // The window doesn't contain the point, keep iterating.
93 LONG ex_styles
= GetWindowLong(hwnd
, GWL_EXSTYLE
);
94 if (ex_styles
& WS_EX_TRANSPARENT
|| ex_styles
& WS_EX_LAYERED
) {
95 // Mouse events fall through WS_EX_TRANSPARENT windows, so we ignore them.
97 // WS_EX_LAYERED is trickier. Apps like Switcher create a totally
98 // transparent WS_EX_LAYERED window that is always on top. If we don't
99 // ignore WS_EX_LAYERED windows and there are totally transparent
100 // WS_EX_LAYERED windows then there are effectively holes on the screen
101 // that the user can't reattach tabs to. So we ignore them. This is a bit
102 // problematic in so far as WS_EX_LAYERED windows need not be totally
103 // transparent in which case we treat chrome windows as not being obscured
104 // when they really are, but this is better than not being able to
109 // hwnd is at the point. Make sure the point is within the windows region.
110 if (GetWindowRgn(hwnd
, tmp_region_
.Get()) == ERROR
) {
111 // There's no region on the window and the window contains the point. Stop
116 // The region is relative to the window's rect.
117 BOOL is_point_in_region
= PtInRegion(tmp_region_
.Get(),
118 screen_loc_
.x() - r
.left
, screen_loc_
.y() - r
.top
);
119 tmp_region_
= CreateRectRgn(0, 0, 0, 0);
120 // Stop iterating if the region contains the point.
121 return !!is_point_in_region
;
125 TopMostFinder(HWND window
,
126 const gfx::Point
& screen_loc
,
127 const std::set
<HWND
>& ignore
)
128 : BaseWindowFinder(ignore
),
131 tmp_region_(CreateRectRgn(0, 0, 0, 0)) {
132 screen_loc_
= gfx::win::DIPToScreenPoint(screen_loc
);
133 EnumWindows(WindowCallbackProc
, as_lparam());
136 // The window we're looking for.
139 // Location of window to find in pixel coordinates.
140 gfx::Point screen_loc_
;
142 // Is target_ the top most window? This is initially false but set to true
143 // in ShouldStopIterating if target_ is passed in.
146 base::win::ScopedRegion tmp_region_
;
148 DISALLOW_COPY_AND_ASSIGN(TopMostFinder
);
151 // WindowFinder ---------------------------------------------------------------
153 // Helper class to determine if a particular point contains a window from our
155 class LocalProcessWindowFinder
: public BaseWindowFinder
{
157 // Returns the hwnd from our process at screen_loc that is not obscured by
158 // another window. Returns NULL otherwise.
159 static gfx::NativeWindow
GetProcessWindowAtPoint(
160 const gfx::Point
& screen_loc
,
161 const std::set
<HWND
>& ignore
) {
162 LocalProcessWindowFinder
finder(screen_loc
, ignore
);
163 // Windows 8 has a window that appears first in the list of iterated
164 // windows, yet is not visually on top of everything.
165 // TODO(sky): figure out a better way to ignore this window.
166 if (finder
.result_
&&
167 ((base::win::OSInfo::GetInstance()->version() >=
168 base::win::VERSION_WIN8
) ||
169 TopMostFinder::IsTopMostWindowAtPoint(finder
.result_
,
172 return views::DesktopWindowTreeHostWin::GetContentWindowForHWND(
179 virtual bool ShouldStopIterating(HWND hwnd
) {
181 if (IsWindowVisible(hwnd
) && GetWindowRect(hwnd
, &r
) &&
182 PtInRect(&r
, screen_loc_
.ToPOINT())) {
190 LocalProcessWindowFinder(const gfx::Point
& screen_loc
,
191 const std::set
<HWND
>& ignore
)
192 : BaseWindowFinder(ignore
),
194 screen_loc_
= gfx::win::DIPToScreenPoint(screen_loc
);
195 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc
, as_lparam());
198 // Position of the mouse in pixel coordinates.
199 gfx::Point screen_loc_
;
201 // The resulting window. This is initially null but set to true in
202 // ShouldStopIterating if an appropriate window is found.
205 DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder
);
208 std::set
<HWND
> RemapIgnoreSet(const std::set
<gfx::NativeView
>& ignore
) {
209 std::set
<HWND
> hwnd_set
;
210 std::set
<gfx::NativeView
>::const_iterator it
= ignore
.begin();
211 for (; it
!= ignore
.end(); ++it
) {
212 HWND w
= (*it
)->GetHost()->GetAcceleratedWidget();
221 gfx::NativeWindow
GetLocalProcessWindowAtPoint(
222 chrome::HostDesktopType host_desktop_type
,
223 const gfx::Point
& screen_point
,
224 const std::set
<gfx::NativeWindow
>& ignore
) {
226 if (host_desktop_type
== chrome::HOST_DESKTOP_TYPE_ASH
)
227 return GetLocalProcessWindowAtPointAsh(screen_point
, ignore
);
229 return LocalProcessWindowFinder::GetProcessWindowAtPoint(
230 screen_point
, RemapIgnoreSet(ignore
));