window_finder_win.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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. 4 5#include "chrome/browser/ui/views/tabs/window_finder.h" 6 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" 13 14#if defined(USE_ASH) 15aura::Window* GetLocalProcessWindowAtPointAsh( 16 const gfx::Point& screen_point, 17 const std::set<aura::Window*>& ignore); 18#endif 19 20namespace { 21 22// BaseWindowFinder ----------------------------------------------------------- 23 24// Base class used to locate a window. This is intended to be used with the 25// various win32 functions that iterate over windows. 26// 27// A subclass need only override ShouldStopIterating to determine when 28// iteration should stop. 29class BaseWindowFinder { 30 public: 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() {} 34 35 protected: 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()) 40 return TRUE; 41 42 return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE; 43 } 44 45 LPARAM as_lparam() { 46 // Cast must match that in WindowCallbackProc(). 47 return reinterpret_cast<LPARAM>(static_cast<BaseWindowFinder*>(this)); 48 } 49 50 // Returns true if iteration should stop, false if iteration should continue. 51 virtual bool ShouldStopIterating(HWND window) = 0; 52 53 private: 54 const std::set<HWND>& ignore_; 55 56 DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder); 57}; 58 59// TopMostFinder -------------------------------------------------------------- 60 61// Helper class to determine if a particular point of a window is not obscured 62// by another window. 63class TopMostFinder : public BaseWindowFinder { 64 public: 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_; 72 } 73 74 virtual bool ShouldStopIterating(HWND hwnd) { 75 if (hwnd == target_) { 76 // Window is topmost, stop iterating. 77 is_top_most_ = true; 78 return true; 79 } 80 81 if (!IsWindowVisible(hwnd)) { 82 // The window isn't visible, keep iterating. 83 return false; 84 } 85 86 RECT r; 87 if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) { 88 // The window doesn't contain the point, keep iterating. 89 return false; 90 } 91 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. 95 // 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 104 // reattach tabs. 105 return false; 106 } 107 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 111 // iterating. 112 return true; 113 } 114 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; 121 } 122 123 private: 124 TopMostFinder(HWND window, 125 const gfx::Point& screen_loc, 126 const std::set<HWND>& ignore) 127 : BaseWindowFinder(ignore), 128 target_(window), 129 screen_loc_(screen_loc), 130 is_top_most_(false), 131 tmp_region_(CreateRectRgn(0, 0, 0, 0)) { 132 EnumWindows(WindowCallbackProc, as_lparam()); 133 } 134 135 // The window we're looking for. 136 HWND target_; 137 138 // Location of window to find. 139 gfx::Point screen_loc_; 140 141 // Is target_ the top most window? This is initially false but set to true 142 // in ShouldStopIterating if target_ is passed in. 143 bool is_top_most_; 144 145 base::win::ScopedRegion tmp_region_; 146 147 DISALLOW_COPY_AND_ASSIGN(TopMostFinder); 148}; 149 150// WindowFinder --------------------------------------------------------------- 151 152// Helper class to determine if a particular point contains a window from our 153// process. 154class LocalProcessWindowFinder : public BaseWindowFinder { 155 public: 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, 169 ignore))) { 170 return views::DesktopWindowTreeHostWin::GetContentWindowForHWND( 171 finder.result_); 172 } 173 return NULL; 174 } 175 176 protected: 177 virtual bool ShouldStopIterating(HWND hwnd) { 178 RECT r; 179 if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) && 180 PtInRect(&r, screen_loc_.ToPOINT())) { 181 result_ = hwnd; 182 return true; 183 } 184 return false; 185 } 186 187 private: 188 LocalProcessWindowFinder(const gfx::Point& screen_loc, 189 const std::set<HWND>& ignore) 190 : BaseWindowFinder(ignore), 191 screen_loc_(screen_loc), 192 result_(NULL) { 193 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam()); 194 } 195 196 // Position of the mouse. 197 gfx::Point screen_loc_; 198 199 // The resulting window. This is initially null but set to true in 200 // ShouldStopIterating if an appropriate window is found. 201 HWND result_; 202 203 DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder); 204}; 205 206std::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(); 211 if (w) 212 hwnd_set.insert(w); 213 } 214 return hwnd_set; 215} 216 217} // namespace 218 219aura::Window* GetLocalProcessWindowAtPoint( 220 chrome::HostDesktopType host_desktop_type, 221 const gfx::Point& screen_point, 222 const std::set<aura::Window*>& ignore) { 223#if defined(USE_ASH) 224 if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH) 225 return GetLocalProcessWindowAtPointAsh(screen_point, ignore); 226#endif 227 return LocalProcessWindowFinder::GetProcessWindowAtPoint( 228 screen_point, RemapIgnoreSet(ignore)); 229} 230