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/views/widget/desktop_aura/desktop_window_tree_host_win.h"
12 #include "ui/views/win/hwnd_util.h"
15 aura::Window* GetLocalProcessWindowAtPointAsh(
16 const gfx::Point& screen_point,
17 const std::set<aura::Window*>& ignore);
22 // BaseWindowFinder -----------------------------------------------------------
24 // Base class used to locate a window. This is intended to be used with the
25 // various win32 functions that iterate over windows.
27 // A subclass need only override ShouldStopIterating to determine when
28 // iteration should stop.
29 class BaseWindowFinder {
31 // Creates a BaseWindowFinder with the specified set of HWNDs to ignore.
32 explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {}
33 virtual ~BaseWindowFinder() {}
36 static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
37 // Cast must match that in as_lparam().
38 BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam);
39 if (finder->ignore_.find(hwnd) != finder->ignore_.end())
42 return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE;
46 // Cast must match that in WindowCallbackProc().
47 return reinterpret_cast<LPARAM>(static_cast<BaseWindowFinder*>(this));
50 // Returns true if iteration should stop, false if iteration should continue.
51 virtual bool ShouldStopIterating(HWND window) = 0;
54 const std::set<HWND>& ignore_;
56 DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder);
59 // TopMostFinder --------------------------------------------------------------
61 // Helper class to determine if a particular point of a window is not obscured
63 class TopMostFinder : public BaseWindowFinder {
65 // Returns true if |window| is the topmost window at the location
66 // |screen_loc|, not including the windows in |ignore|.
67 static bool IsTopMostWindowAtPoint(HWND window,
68 const gfx::Point& screen_loc,
69 const std::set<HWND>& ignore) {
70 TopMostFinder finder(window, screen_loc, ignore);
71 return finder.is_top_most_;
74 virtual bool ShouldStopIterating(HWND hwnd) {
75 if (hwnd == target_) {
76 // Window is topmost, stop iterating.
81 if (!IsWindowVisible(hwnd)) {
82 // The window isn't visible, keep iterating.
87 if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) {
88 // The window doesn't contain the point, keep iterating.
92 LONG ex_styles = GetWindowLong(hwnd, GWL_EXSTYLE);
93 if (ex_styles & WS_EX_TRANSPARENT || ex_styles & WS_EX_LAYERED) {
94 // Mouse events fall through WS_EX_TRANSPARENT windows, so we ignore them.
96 // WS_EX_LAYERED is trickier. Apps like Switcher create a totally
97 // transparent WS_EX_LAYERED window that is always on top. If we don't
98 // ignore WS_EX_LAYERED windows and there are totally transparent
99 // WS_EX_LAYERED windows then there are effectively holes on the screen
100 // that the user can't reattach tabs to. So we ignore them. This is a bit
101 // problematic in so far as WS_EX_LAYERED windows need not be totally
102 // transparent in which case we treat chrome windows as not being obscured
103 // when they really are, but this is better than not being able to
108 // hwnd is at the point. Make sure the point is within the windows region.
109 if (GetWindowRgn(hwnd, tmp_region_.Get()) == ERROR) {
110 // There's no region on the window and the window contains the point. Stop
115 // The region is relative to the window's rect.
116 BOOL is_point_in_region = PtInRegion(tmp_region_.Get(),
117 screen_loc_.x() - r.left, screen_loc_.y() - r.top);
118 tmp_region_ = CreateRectRgn(0, 0, 0, 0);
119 // Stop iterating if the region contains the point.
120 return !!is_point_in_region;
124 TopMostFinder(HWND window,
125 const gfx::Point& screen_loc,
126 const std::set<HWND>& ignore)
127 : BaseWindowFinder(ignore),
129 screen_loc_(screen_loc),
131 tmp_region_(CreateRectRgn(0, 0, 0, 0)) {
132 EnumWindows(WindowCallbackProc, as_lparam());
135 // The window we're looking for.
138 // Location of window to find.
139 gfx::Point screen_loc_;
141 // Is target_ the top most window? This is initially false but set to true
142 // in ShouldStopIterating if target_ is passed in.
145 base::win::ScopedRegion tmp_region_;
147 DISALLOW_COPY_AND_ASSIGN(TopMostFinder);
150 // WindowFinder ---------------------------------------------------------------
152 // Helper class to determine if a particular point contains a window from our
154 class LocalProcessWindowFinder : public BaseWindowFinder {
156 // Returns the hwnd from our process at screen_loc that is not obscured by
157 // another window. Returns NULL otherwise.
158 static gfx::NativeWindow GetProcessWindowAtPoint(
159 const gfx::Point& screen_loc,
160 const std::set<HWND>& ignore) {
161 LocalProcessWindowFinder finder(screen_loc, ignore);
162 // Windows 8 has a window that appears first in the list of iterated
163 // windows, yet is not visually on top of everything.
164 // TODO(sky): figure out a better way to ignore this window.
165 if (finder.result_ &&
166 ((base::win::OSInfo::GetInstance()->version() >=
167 base::win::VERSION_WIN8) ||
168 TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc,
170 return views::DesktopWindowTreeHostWin::GetContentWindowForHWND(
177 virtual bool ShouldStopIterating(HWND hwnd) {
179 if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) &&
180 PtInRect(&r, screen_loc_.ToPOINT())) {
188 LocalProcessWindowFinder(const gfx::Point& screen_loc,
189 const std::set<HWND>& ignore)
190 : BaseWindowFinder(ignore),
191 screen_loc_(screen_loc),
193 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam());
196 // Position of the mouse.
197 gfx::Point screen_loc_;
199 // The resulting window. This is initially null but set to true in
200 // ShouldStopIterating if an appropriate window is found.
203 DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder);
206 std::set<HWND> RemapIgnoreSet(const std::set<gfx::NativeView>& ignore) {
207 std::set<HWND> hwnd_set;
208 std::set<gfx::NativeView>::const_iterator it = ignore.begin();
209 for (; it != ignore.end(); ++it) {
210 HWND w = (*it)->GetDispatcher()->host()->GetAcceleratedWidget();
219 aura::Window* GetLocalProcessWindowAtPoint(
220 chrome::HostDesktopType host_desktop_type,
221 const gfx::Point& screen_point,
222 const std::set<aura::Window*>& ignore) {
224 if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
225 return GetLocalProcessWindowAtPointAsh(screen_point, ignore);
227 return LocalProcessWindowFinder::GetProcessWindowAtPoint(
228 screen_point, RemapIgnoreSet(ignore));