#include "ui/aura/native_window_occlusion_tracker_win.h"
#include <dwmapi.h>
#include <powersetting.h>
#include <memory>
#include <string>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_util_win.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/win/hwnd_util.h"
namespace aura {
namespace {
const base::TimeDelta kUpdateOcclusionDelayMin = base::Milliseconds(16);
const base::TimeDelta kUpdateOcclusionDelayMax = base::Milliseconds(100);
NativeWindowOcclusionTrackerWin* g_tracker = nullptr;
SkRegion AdjustForClientAndConvertToDips(
const SkRegion& region_pixels,
float scale_factor,
const gfx::Rect& window_tree_host_bounds_pixels) {
if (region_pixels.isEmpty())
return region_pixels;
SkRegion region(region_pixels);
region.op(gfx::RectToSkIRect(window_tree_host_bounds_pixels),
SkRegion::kIntersect_Op);
region.translate(window_tree_host_bounds_pixels.x(),
window_tree_host_bounds_pixels.y());
if (region.isEmpty() || scale_factor == 1.0f)
return region;
SkRegion::Iterator iter(region);
std::vector<SkIRect> rects;
while (!iter.done()) {
const gfx::Rect rect_pixels = gfx::SkIRectToRect(iter.rect());
rects.push_back(gfx::RectToSkIRect(gfx::ScaleToEnclosedRect(
rect_pixels, 1.0f / scale_factor, 1.0f / scale_factor)));
iter.next();
}
SkRegion result;
result.setRects(rects.data(), rects.size());
return result;
}
}
NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator*
NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::instance_ =
nullptr;
NativeWindowOcclusionTrackerWin*
NativeWindowOcclusionTrackerWin::GetOrCreateInstance() {
if (!g_tracker)
g_tracker = new NativeWindowOcclusionTrackerWin();
return g_tracker;
}
void NativeWindowOcclusionTrackerWin::DeleteInstanceForTesting() {
delete g_tracker;
g_tracker = nullptr;
}
void NativeWindowOcclusionTrackerWin::Enable(Window* window) {
DCHECK(window->IsRootWindow());
if (window->HasObserver(this)) {
NOTREACHED() << "window shouldn't already be observing occlusion tracker";
}
HWND root_window_hwnd = window->GetHost()->GetAcceleratedWidget();
if (IsIconic(root_window_hwnd)) {
return;
}
window->AddObserver(this);
hwnd_root_window_map_[root_window_hwnd] = window;
update_occlusion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WindowOcclusionCalculator::EnableOcclusionTrackingForWindow,
base::Unretained(WindowOcclusionCalculator::GetInstance()),
root_window_hwnd));
}
void NativeWindowOcclusionTrackerWin::Disable(Window* window) {
DCHECK(window->IsRootWindow());
HWND root_window_hwnd = window->GetHost()->GetAcceleratedWidget();
DCHECK(root_window_hwnd);
hwnd_root_window_map_.erase(root_window_hwnd);
window->RemoveObserver(this);
update_occlusion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WindowOcclusionCalculator::DisableOcclusionTrackingForWindow,
base::Unretained(WindowOcclusionCalculator::GetInstance()),
root_window_hwnd));
}
void NativeWindowOcclusionTrackerWin::OnWindowVisibilityChanged(Window* window,
bool visible) {
if (!window->IsRootWindow())
return;
window->GetHost()->SetNativeWindowOcclusionState(
visible ? Window::OcclusionState::UNKNOWN
: Window::OcclusionState::HIDDEN,
{});
update_occlusion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WindowOcclusionCalculator::HandleVisibilityChanged,
base::Unretained(WindowOcclusionCalculator::GetInstance()),
visible));
}
void NativeWindowOcclusionTrackerWin::OnWindowDestroying(Window* window) {
Disable(window);
}
NativeWindowOcclusionTrackerWin::NativeWindowOcclusionTrackerWin()
:
update_occlusion_task_runner_(base::ThreadPool::CreateCOMSTATaskRunner(
{base::MayBlock(),
base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
session_change_observer_(
base::BindRepeating(&NativeWindowOcclusionTrackerWin::OnSessionChange,
base::Unretained(this))),
power_setting_change_listener_(this) {
WindowOcclusionCalculator::CreateInstance(
update_occlusion_task_runner_,
base::SequencedTaskRunner::GetCurrentDefault(),
base::BindRepeating(
&NativeWindowOcclusionTrackerWin::UpdateOcclusionState,
weak_factory_.GetWeakPtr()));
}
NativeWindowOcclusionTrackerWin::~NativeWindowOcclusionTrackerWin() {
DCHECK(hwnd_root_window_map_.empty())
<< "Occlusion tracker torn down while a Window still exists";
base::WaitableEvent done_event;
update_occlusion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WindowOcclusionCalculator::DeleteInstanceForTesting,
&done_event));
done_event.Wait();
}
void NativeWindowOcclusionTrackerWin::UpdateOcclusionState(
const HwndToRootOcclusionStateMap& root_window_hwnds_occlusion_state,
bool show_all_windows) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
num_visible_root_windows_ = 0;
for (const auto& root_window_pair : root_window_hwnds_occlusion_state) {
auto it = hwnd_root_window_map_.find(root_window_pair.first);
if (it == hwnd_root_window_map_.end())
continue;
it->second->GetHost()->set_on_current_workspace(
root_window_pair.second.on_current_workspace);
if (!it->second->IsVisible()) {
it->second->GetHost()->SetNativeWindowOcclusionState(
Window::OcclusionState::HIDDEN, {});
continue;
}
Window::OcclusionState occl_state = root_window_pair.second.occlusion_state;
SkRegion occluded_region;
if (screen_locked_ || !display_on_) {
occl_state = Window::OcclusionState::OCCLUDED;
} else if (show_all_windows) {
occl_state = Window::OcclusionState::VISIBLE;
} else if (occl_state == Window::OcclusionState::VISIBLE) {
occluded_region = AdjustForClientAndConvertToDips(
root_window_pair.second.occluded_region_pixels,
it->second->GetHost()->device_scale_factor(),
it->second->GetHost()
->GetBoundsInAcceleratedWidgetPixelCoordinates());
}
it->second->GetHost()->SetNativeWindowOcclusionState(occl_state,
occluded_region);
num_visible_root_windows_++;
}
}
void NativeWindowOcclusionTrackerWin::OnSessionChange(
WPARAM status_code,
const bool* is_current_session) {
if (is_current_session && !*is_current_session)
return;
if (status_code == WTS_SESSION_UNLOCK) {
screen_locked_ = false;
update_occlusion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WindowOcclusionCalculator::ForceRecalculation,
base::Unretained(WindowOcclusionCalculator::GetInstance())));
} else if (status_code == WTS_SESSION_LOCK && is_current_session) {
screen_locked_ = true;
MarkNonIconicWindowsOccluded();
}
}
void NativeWindowOcclusionTrackerWin::OnDisplayStateChanged(bool display_on) {
if (display_on == display_on_)
return;
display_on_ = display_on;
if (display_on_) {
update_occlusion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WindowOcclusionCalculator::ForceRecalculation,
base::Unretained(WindowOcclusionCalculator::GetInstance())));
} else {
MarkNonIconicWindowsOccluded();
}
}
void NativeWindowOcclusionTrackerWin::OnResume() {
update_occlusion_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WindowOcclusionCalculator::HandleResumeSuspend,
base::Unretained(
WindowOcclusionCalculator::GetInstance())));
}
void NativeWindowOcclusionTrackerWin::OnSuspend() {
update_occlusion_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WindowOcclusionCalculator::HandleResumeSuspend,
base::Unretained(
WindowOcclusionCalculator::GetInstance())));
}
void NativeWindowOcclusionTrackerWin::MarkNonIconicWindowsOccluded() {
for (const auto& root_window_hwnd_pair : hwnd_root_window_map_) {
root_window_hwnd_pair.second->GetHost()->SetNativeWindowOcclusionState(
IsIconic(root_window_hwnd_pair.first)
? Window::OcclusionState::HIDDEN
: Window::OcclusionState::OCCLUDED,
{});
}
}
NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
WindowOcclusionCalculator(
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner,
UpdateOcclusionStateCallback update_occlusion_state_callback)
: task_runner_(task_runner),
ui_thread_task_runner_(ui_thread_task_runner),
update_occlusion_state_callback_(update_occlusion_state_callback) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
~WindowOcclusionCalculator() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UnregisterEventHooks();
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::CreateInstance(
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner,
UpdateOcclusionStateCallback update_occlusion_state_callback) {
DCHECK(!instance_);
instance_ = new WindowOcclusionCalculator(task_runner, ui_thread_task_runner,
update_occlusion_state_callback);
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
DeleteInstanceForTesting(base::WaitableEvent* done_event) {
DCHECK(instance_);
delete instance_;
instance_ = nullptr;
done_event->Signal();
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
EnableOcclusionTrackingForWindow(HWND hwnd) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
root_window_hwnds_occlusion_state_[hwnd] = {};
if (global_event_hooks_.empty())
RegisterEventHooks();
ScheduleOcclusionCalculationIfNeeded();
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
DisableOcclusionTrackingForWindow(HWND hwnd) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
root_window_hwnds_occlusion_state_.erase(hwnd);
if (moving_window_ == hwnd)
moving_window_ = 0;
if (root_window_hwnds_occlusion_state_.empty()) {
UnregisterEventHooks();
if (occlusion_update_timer_.IsRunning())
occlusion_update_timer_.Stop();
}
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
HandleVisibilityChanged(bool visible) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (visible) {
MaybeRegisterEventHooks();
ScheduleOcclusionCalculationIfNeeded();
}
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ForceRecalculation() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
MaybeRegisterEventHooks();
ScheduleOcclusionCalculationIfNeeded();
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
HandleResumeSuspend() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (occlusion_update_timer_.IsRunning())
occlusion_update_timer_.Stop();
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
MaybeRegisterEventHooks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (global_event_hooks_.empty())
RegisterEventHooks();
}
void CALLBACK
NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::EventHookCallback(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime) {
if (instance_)
instance_->ProcessEventHookCallback(event, hwnd, idObject, idChild);
}
BOOL CALLBACK NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ComputeNativeWindowOcclusionStatusCallback(HWND hwnd, LPARAM lParam) {
if (instance_) {
return instance_->ProcessComputeNativeWindowOcclusionStatusCallback(
hwnd, reinterpret_cast<base::flat_set<DWORD>*>(lParam));
}
return FALSE;
}
BOOL CALLBACK NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
UpdateVisibleWindowProcessIdsCallback(HWND hwnd, LPARAM lParam) {
if (instance_) {
instance_->ProcessUpdateVisibleWindowProcessIdsCallback(hwnd);
return TRUE;
}
return FALSE;
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
UpdateVisibleWindowProcessIds() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pids_for_location_change_hook_.clear();
EnumWindows(&UpdateVisibleWindowProcessIdsCallback, 0);
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ComputeNativeWindowOcclusionStatus() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (root_window_hwnds_occlusion_state_.empty())
return;
bool should_unregister_event_hooks = true;
int screen_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screen_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
SkRegion screen_region = SkRegion(
SkIRect::MakeLTRB(screen_left, screen_top,
screen_left + GetSystemMetrics(SM_CXVIRTUALSCREEN),
screen_top + GetSystemMetrics(SM_CYVIRTUALSCREEN)));
num_root_windows_with_unknown_occlusion_state_ = 0;
for (auto& root_window_pair : root_window_hwnds_occlusion_state_) {
HWND hwnd = root_window_pair.first;
root_window_pair.second = {};
root_window_pair.second.on_current_workspace =
IsWindowOnCurrentVirtualDesktop(hwnd);
if (IsIconic(hwnd)) {
root_window_pair.second.occlusion_state = Window::OcclusionState::HIDDEN;
} else if (root_window_pair.second.on_current_workspace == false) {
root_window_pair.second.occlusion_state =
Window::OcclusionState::OCCLUDED;
should_unregister_event_hooks = false;
} else {
root_window_pair.second.occlusion_state = Window::OcclusionState::UNKNOWN;
should_unregister_event_hooks = false;
num_root_windows_with_unknown_occlusion_state_++;
}
}
if (should_unregister_event_hooks) {
UnregisterEventHooks();
} else {
base::flat_set<DWORD> current_pids_with_visible_windows;
unoccluded_desktop_region_ = screen_region;
EnumWindows(&ComputeNativeWindowOcclusionStatusCallback,
reinterpret_cast<LPARAM>(¤t_pids_with_visible_windows));
base::flat_set<DWORD> pids_to_remove;
for (auto loc_change_pid : pids_for_location_change_hook_) {
if (current_pids_with_visible_windows.find(loc_change_pid) ==
current_pids_with_visible_windows.end()) {
UnhookWinEvent(process_event_hooks_[loc_change_pid]);
process_event_hooks_.erase(loc_change_pid);
pids_to_remove.insert(loc_change_pid);
}
}
if (!pids_to_remove.empty()) {
base::EraseIf(pids_for_location_change_hook_,
[&pids_to_remove](DWORD pid) {
return pids_to_remove.find(pid) != pids_to_remove.end();
});
}
}
ui_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(update_occlusion_state_callback_,
root_window_hwnds_occlusion_state_, showing_thumbnails_));
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ScheduleOcclusionCalculationIfNeeded() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!occlusion_update_timer_.IsRunning()) {
occlusion_update_timer_.Start(
FROM_HERE, GetUpdateOcclusionDelay(), this,
&WindowOcclusionCalculator::ComputeNativeWindowOcclusionStatus);
}
}
const base::TimeDelta NativeWindowOcclusionTrackerWin::
WindowOcclusionCalculator::GetUpdateOcclusionDelay() {
return moving_window_ ? kUpdateOcclusionDelayMax : kUpdateOcclusionDelayMin;
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
RegisterGlobalEventHook(UINT event_min, UINT event_max) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
HWINEVENTHOOK event_hook =
SetWinEventHook(event_min, event_max, nullptr, &EventHookCallback, 0, 0,
WINEVENT_OUTOFCONTEXT);
global_event_hooks_.push_back(event_hook);
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
RegisterEventHookForProcess(DWORD pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pids_for_location_change_hook_.insert(pid);
process_event_hooks_[pid] = SetWinEventHook(
EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, nullptr,
&EventHookCallback, pid, 0, WINEVENT_OUTOFCONTEXT);
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
RegisterEventHooks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(global_event_hooks_.empty());
RegisterGlobalEventHook(EVENT_SYSTEM_CAPTUREEND, EVENT_SYSTEM_CAPTUREEND);
RegisterGlobalEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND);
RegisterGlobalEventHook(EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZEEND);
RegisterGlobalEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND);
RegisterGlobalEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE);
RegisterGlobalEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE);
RegisterGlobalEventHook(EVENT_OBJECT_CLOAKED, EVENT_OBJECT_UNCLOAKED);
UpdateVisibleWindowProcessIds();
for (DWORD pid : pids_for_location_change_hook_)
RegisterEventHookForProcess(pid);
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
UnregisterEventHooks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
window_is_moving_ = false;
for (HWINEVENTHOOK event_hook : global_event_hooks_)
UnhookWinEvent(event_hook);
global_event_hooks_.clear();
for (DWORD pid : pids_for_location_change_hook_)
UnhookWinEvent(process_event_hooks_[pid]);
process_event_hooks_.clear();
pids_for_location_change_hook_.clear();
}
bool NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ProcessComputeNativeWindowOcclusionStatusCallback(
HWND hwnd,
base::flat_set<DWORD>* current_pids_with_visible_windows) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gfx::Rect window_rect;
const bool window_is_occluding =
WindowCanOccludeOtherWindowsOnCurrentVirtualDesktop(hwnd, &window_rect);
if (window_is_occluding) {
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
current_pids_with_visible_windows->insert(pid);
if (!base::Contains(process_event_hooks_, pid))
RegisterEventHookForProcess(pid);
}
if (hwnd == moving_window_)
return true;
if (num_root_windows_with_unknown_occlusion_state_ == 0)
return true;
auto it = root_window_hwnds_occlusion_state_.find(hwnd);
if (it == root_window_hwnds_occlusion_state_.end() ||
it->second.occlusion_state != Window::OcclusionState::UNKNOWN) {
if (window_is_occluding) {
unoccluded_desktop_region_.op(gfx::RectToSkIRect(window_rect),
SkRegion::kDifference_Op);
}
return true;
}
num_root_windows_with_unknown_occlusion_state_--;
SkRegion curr_unoccluded_destkop = unoccluded_desktop_region_;
if (window_is_occluding) {
unoccluded_desktop_region_.op(gfx::RectToSkIRect(window_rect),
SkRegion::kDifference_Op);
}
if (!window_is_occluding) {
RECT rect;
if (::GetWindowRect(hwnd, &rect) != 0) {
SkRegion window_region(
SkIRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom));
window_rect = gfx::Rect(rect);
curr_unoccluded_destkop.op(window_region, SkRegion::kDifference_Op);
}
}
if (unoccluded_desktop_region_ == curr_unoccluded_destkop) {
it->second.occlusion_state = Window::OcclusionState::OCCLUDED;
return true;
}
it->second.occlusion_state = Window::OcclusionState::VISIBLE;
return true;
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ProcessEventHookCallback(DWORD event,
HWND hwnd,
LONG id_object,
LONG id_child) {
if (!hwnd)
return;
if (id_object != OBJID_WINDOW)
return;
bool calculate_occlusion = true;
if (::GetWindowLong(hwnd, GWL_STYLE) & WS_POPUP) {
std::wstring hwnd_class_name = gfx::GetClassName(hwnd);
calculate_occlusion = hwnd_class_name.starts_with(L"Chrome_WidgetWin_") ||
hwnd_class_name == L"Shell_TrayWnd";
}
if (event == EVENT_OBJECT_SHOW) {
if (showing_thumbnails_)
return;
std::string hwnd_class_name = base::WideToUTF8(gfx::GetClassName(hwnd));
if ((hwnd_class_name == "MultitaskingViewFrame" ||
hwnd_class_name == "TaskListThumbnailWnd")) {
showing_thumbnails_ = true;
ui_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(update_occlusion_state_callback_,
root_window_hwnds_occlusion_state_,
showing_thumbnails_));
}
return;
} else if (event == EVENT_OBJECT_HIDE) {
if (!showing_thumbnails_)
return;
std::string hwnd_class_name = base::WideToUTF8(gfx::GetClassName(hwnd));
if (hwnd_class_name == "MultitaskingViewFrame" ||
hwnd_class_name == "TaskListThumbnailWnd") {
showing_thumbnails_ = false;
calculate_occlusion = true;
} else {
return;
}
}
else if (event == EVENT_SYSTEM_MOVESIZESTART) {
moving_window_ = hwnd;
} else if (event == EVENT_SYSTEM_MOVESIZEEND) {
moving_window_ = 0;
} else if (moving_window_ != 0) {
if (event == EVENT_OBJECT_LOCATIONCHANGE ||
event == EVENT_OBJECT_STATECHANGE) {
if ((root_window_hwnds_occlusion_state_.size() <= 1) ||
(root_window_hwnds_occlusion_state_.find(hwnd) ==
root_window_hwnds_occlusion_state_.end())) {
return;
}
} else {
moving_window_ = 0;
}
}
if (!calculate_occlusion)
return;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WindowOcclusionCalculator::ScheduleOcclusionCalculationIfNeeded,
weak_factory_.GetWeakPtr()));
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
ProcessUpdateVisibleWindowProcessIdsCallback(HWND hwnd) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gfx::Rect window_rect;
if (WindowCanOccludeOtherWindowsOnCurrentVirtualDesktop(hwnd, &window_rect)) {
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
pids_for_location_change_hook_.insert(pid);
}
}
bool NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
WindowCanOccludeOtherWindowsOnCurrentVirtualDesktop(
HWND hwnd,
gfx::Rect* window_rect) {
return gfx::IsWindowVisibleAndFullyOpaque(hwnd, window_rect) &&
(IsWindowOnCurrentVirtualDesktop(hwnd) == true);
}
std::optional<bool> NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
IsWindowOnCurrentVirtualDesktop(HWND hwnd) {
if (!gfx::IsWindowCloaked(hwnd))
return true;
if (!virtual_desktop_manager_) {
if (FAILED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(&virtual_desktop_manager_)))) {
return std::nullopt;
}
}
return gfx::IsWindowOnCurrentVirtualDesktop(hwnd, virtual_desktop_manager_);
}
}