blob: a4e1298dc86fb3f106295136f87faf9afc62b892 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/aura/window_tree_host_platform.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/notimplemented.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/env.h"
#include "ui/aura/host_frame_rate_throttler.h"
#include "ui/aura/native_window_occlusion_tracker.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host_observer.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/layout.h"
#include "ui/base/view_prop.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/keyboard_hook.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/platform_window/platform_window.h"
#include "ui/platform_window/platform_window_init_properties.h"
#if BUILDFLAG(IS_OZONE)
#include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
#include "ui/events/ozone/events_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "ui/platform_window/win/win_window.h"
#endif
namespace aura {
namespace {
WindowTreeHostPlatform::PlatformWindowFactoryDelegateForTesting*
g_platform_window_factory_delegate_for_testing = nullptr;
const char kWindowTreeHostPlatformForAcceleratedWidget[] =
"__AURA_WINDOW_TREE_HOST_PLATFORM_ACCELERATED_WIDGET__";
}
// static
std::unique_ptr<WindowTreeHost> WindowTreeHost::Create(
ui::PlatformWindowInitProperties properties) {
return std::make_unique<WindowTreeHostPlatform>(
std::move(properties),
std::make_unique<aura::Window>(nullptr, client::WINDOW_TYPE_UNKNOWN));
}
WindowTreeHostPlatform::WindowTreeHostPlatform(
ui::PlatformWindowInitProperties properties,
std::unique_ptr<Window> window)
: WindowTreeHost(std::move(window)),
widget_(gfx::kNullAcceleratedWidget),
current_cursor_(ui::mojom::CursorType::kNull) {
size_in_pixels_ = properties.bounds.size();
CreateCompositor(false, false, properties.enable_compositing_based_throttling,
properties.compositor_memory_limit_mb);
CreateAndSetPlatformWindow(std::move(properties));
}
WindowTreeHostPlatform::WindowTreeHostPlatform(std::unique_ptr<Window> window)
: WindowTreeHost(std::move(window)),
widget_(gfx::kNullAcceleratedWidget),
current_cursor_(ui::mojom::CursorType::kNull) {}
// static
WindowTreeHostPlatform* WindowTreeHostPlatform::GetHostForWindow(
aura::Window* window) {
return reinterpret_cast<WindowTreeHostPlatform*>(
ui::ViewProp::GetValue(window->GetHost()->GetAcceleratedWidget(),
kWindowTreeHostPlatformForAcceleratedWidget));
}
void WindowTreeHostPlatform::CreateAndSetPlatformWindow(
ui::PlatformWindowInitProperties properties) {
// Cache initial size used to create |platform_window_| so that it does not
// end up propagating unneeded bounds change event when it is first notified
// through OnBoundsChanged, which may lead to unneeded re-layouts, etc.
size_in_pixels_ = properties.bounds.size();
platform_window_ = CreatePlatformWindow(std::move(properties));
}
void WindowTreeHostPlatform::SetPlatformWindow(
std::unique_ptr<ui::PlatformWindow> window) {
platform_window_ = std::move(window);
}
WindowTreeHostPlatform::~WindowTreeHostPlatform() {
DestroyCompositor();
DestroyDispatcher();
// |platform_window_| may not exist yet.
if (platform_window_)
platform_window_->Close();
}
ui::EventSource* WindowTreeHostPlatform::GetEventSource() {
return this;
}
gfx::AcceleratedWidget WindowTreeHostPlatform::GetAcceleratedWidget() {
return widget_;
}
void WindowTreeHostPlatform::ShowImpl() {
platform_window_->Show();
}
void WindowTreeHostPlatform::HideImpl() {
platform_window_->Hide();
}
gfx::Rect WindowTreeHostPlatform::GetBoundsInPixels() const {
return platform_window_->GetBoundsInPixels();
}
void WindowTreeHostPlatform::SetBoundsInPixels(const gfx::Rect& bounds) {
platform_window_->SetBoundsInPixels(bounds);
}
void WindowTreeHostPlatform::SetCapture() {
#if BUILDFLAG(IS_OZONE)
if (ui::IsNativeUiEventDispatchDisabled()) {
return;
}
#endif
platform_window_->SetCapture();
}
void WindowTreeHostPlatform::ReleaseCapture() {
platform_window_->ReleaseCapture();
}
gfx::Point WindowTreeHostPlatform::GetLocationOnScreenInPixels() const {
return platform_window_->GetBoundsInPixels().origin();
}
bool WindowTreeHostPlatform::CaptureSystemKeyEventsImpl(
std::optional<base::flat_set<ui::DomCode>> dom_codes) {
// Only one KeyboardHook should be active at a time, otherwise there will be
// problems with event routing (i.e. which Hook takes precedence) and
// destruction ordering.
DCHECK(!keyboard_hook_);
keyboard_hook_ = ui::KeyboardHook::CreateModifierKeyboardHook(
std::move(dom_codes), GetAcceleratedWidget(),
base::BindRepeating(
[](ui::PlatformWindowDelegate* delegate, ui::KeyEvent* event) {
delegate->DispatchEvent(event);
},
base::Unretained(this)));
return keyboard_hook_ != nullptr;
}
void WindowTreeHostPlatform::ReleaseSystemKeyEventCapture() {
keyboard_hook_.reset();
}
bool WindowTreeHostPlatform::IsKeyLocked(ui::DomCode dom_code) {
return keyboard_hook_ && keyboard_hook_->IsKeyLocked(dom_code);
}
base::flat_map<std::string, std::string>
WindowTreeHostPlatform::GetKeyboardLayoutMap() {
#if BUILDFLAG(IS_OZONE)
return ui::GenerateDomKeyboardLayoutMap();
#else
NOTIMPLEMENTED();
return {};
#endif
}
void WindowTreeHostPlatform::OnVideoCaptureLockCreated() {
if (platform_window_) {
platform_window_->SetVideoCapture();
}
}
void WindowTreeHostPlatform::OnVideoCaptureLockDestroyed() {
if (platform_window_) {
platform_window_->ReleaseVideoCapture();
}
}
void WindowTreeHostPlatform::SetCursorNative(gfx::NativeCursor cursor) {
if (cursor == current_cursor_)
return;
current_cursor_ = cursor;
platform_window_->SetCursor(cursor.platform());
}
void WindowTreeHostPlatform::MoveCursorToScreenLocationInPixels(
const gfx::Point& location_in_pixels) {
#if BUILDFLAG(IS_OZONE)
if (ui::IsNativeUiEventDispatchDisabled()) {
// Unit tests should not test or rely on the native cursor position because
// it is shared between multiple tests.
return;
}
#endif
platform_window_->MoveCursorTo(location_in_pixels);
}
void WindowTreeHostPlatform::OnCursorVisibilityChangedNative(bool show) {
NOTIMPLEMENTED_LOG_ONCE();
}
void WindowTreeHostPlatform::LockMouse(Window* window) {
window->SetCapture();
WindowTreeHost::LockMouse(window);
}
std::unique_ptr<ui::PlatformWindow>
WindowTreeHostPlatform::CreatePlatformWindow(
ui::PlatformWindowInitProperties properties) {
if (g_platform_window_factory_delegate_for_testing) {
return g_platform_window_factory_delegate_for_testing->Create(this);
}
#if BUILDFLAG(IS_OZONE)
return ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
this, std::move(properties));
#elif BUILDFLAG(IS_WIN)
return std::make_unique<ui::WinWindow>(this, properties.bounds);
#else
NOTIMPLEMENTED();
return nullptr;
#endif
}
// static
void WindowTreeHostPlatform::SetPlatformWindowFactoryDelegateForTesting(
PlatformWindowFactoryDelegateForTesting* delegate) {
CHECK_IS_TEST();
g_platform_window_factory_delegate_for_testing = delegate;
}
void WindowTreeHostPlatform::OnBoundsChanged(const BoundsChange& change) {
// It's possible this function may be called recursively. Only notify
// observers on initial entry. This way observers can safely assume that
// OnHostDidProcessBoundsChange() is called when all bounds changes have
// completed.
if (++on_bounds_changed_recursion_depth_ == 1) {
observers().Notify(&WindowTreeHostObserver::OnHostWillProcessBoundsChange,
this);
}
const auto preferred_scale =
display::Screen::GetScreen()->GetPreferredScaleFactorForWindow(window());
float current_scale = compositor()->device_scale_factor();
float new_scale = preferred_scale.value_or(1.0f);
auto weak_ref = GetWeakPtr();
auto new_size = GetBoundsInPixels().size();
bool size_changed = size_in_pixels_ != new_size;
size_in_pixels_ = new_size;
if (change.origin_changed) {
OnHostMovedInPixels();
// Changing the bounds may destroy this.
if (!weak_ref)
return;
}
if (size_changed || current_scale != new_scale) {
OnHostResizedInPixels(new_size);
// Changing the size may destroy this.
if (!weak_ref)
return;
}
DCHECK_GT(on_bounds_changed_recursion_depth_, 0);
if (--on_bounds_changed_recursion_depth_ == 0) {
observers().Notify(&WindowTreeHostObserver::OnHostDidProcessBoundsChange,
this);
}
}
void WindowTreeHostPlatform::OnDamageRect(const gfx::Rect& damage_rect) {
compositor()->ScheduleRedrawRect(damage_rect);
}
void WindowTreeHostPlatform::DispatchEvent(ui::Event* event) {
TRACE_EVENT0("input", "WindowTreeHostPlatform::DispatchEvent");
ui::EventDispatchDetails details = SendEventToSink(event);
if (details.dispatcher_destroyed)
event->SetHandled();
}
void WindowTreeHostPlatform::OnCloseRequest() {
OnHostCloseRequested();
}
void WindowTreeHostPlatform::OnClosed() {}
void WindowTreeHostPlatform::OnWindowStateChanged(
ui::PlatformWindowState old_state,
ui::PlatformWindowState new_state) {}
void WindowTreeHostPlatform::OnLostCapture() {
OnHostLostWindowCapture();
}
void WindowTreeHostPlatform::OnAcceleratedWidgetAvailable(
gfx::AcceleratedWidget widget) {
prop_ = std::make_unique<ui::ViewProp>(
widget, kWindowTreeHostPlatformForAcceleratedWidget, this);
widget_ = widget;
// This may be called before the Compositor has been created.
if (compositor())
WindowTreeHost::OnAcceleratedWidgetAvailable();
}
void WindowTreeHostPlatform::OnWillDestroyAcceleratedWidget() {}
void WindowTreeHostPlatform::OnAcceleratedWidgetDestroyed() {
gfx::AcceleratedWidget widget = compositor()->ReleaseAcceleratedWidget();
DCHECK_EQ(widget, widget_);
widget_ = gfx::kNullAcceleratedWidget;
}
void WindowTreeHostPlatform::OnActivationChanged(bool active) {}
void WindowTreeHostPlatform::OnCursorUpdate() {
client::CursorClient* cursor_client = client::GetCursorClient(window());
if (cursor_client) {
auto display =
display::Screen::GetScreen()->GetDisplayNearestWindow(window());
DCHECK(display.is_valid());
cursor_client->SetDisplay(display);
}
}
void WindowTreeHostPlatform::OnOcclusionStateChanged(
ui::PlatformWindowOcclusionState occlusion_state) {
auto aura_occlusion_state = Window::OcclusionState::UNKNOWN;
switch (occlusion_state) {
case ui::PlatformWindowOcclusionState::kUnknown:
aura_occlusion_state = Window::OcclusionState::UNKNOWN;
break;
case ui::PlatformWindowOcclusionState::kVisible:
aura_occlusion_state = Window::OcclusionState::VISIBLE;
break;
case ui::PlatformWindowOcclusionState::kOccluded:
aura_occlusion_state = Window::OcclusionState::OCCLUDED;
break;
case ui::PlatformWindowOcclusionState::kHidden:
aura_occlusion_state = Window::OcclusionState::HIDDEN;
break;
}
SetNativeWindowOcclusionState(aura_occlusion_state, {});
}
int64_t WindowTreeHostPlatform::OnStateUpdate(
const PlatformWindowDelegate::State& old,
const PlatformWindowDelegate::State& latest) {
if (old.window_state != latest.window_state) {
OnWindowStateChanged(old.window_state, latest.window_state);
}
if (old.bounds_dip != latest.bounds_dip || old.size_px != latest.size_px ||
old.window_scale != latest.window_scale) {
bool origin_changed = old.bounds_dip.origin() != latest.bounds_dip.origin();
OnBoundsChanged({origin_changed});
}
bool needs_frame = latest.WillProduceFrameOnUpdateFrom(old);
if (old.occlusion_state != latest.occlusion_state &&
NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) {
const bool visible_before = compositor()->IsVisible();
OnOcclusionStateChanged(latest.occlusion_state);
if (!visible_before && compositor()->IsVisible()) {
// If the compositor has become visible, make sure to wait for a frame.
needs_frame = true;
}
}
// Only set the sequence ID if this change will produce a frame.
// If it won't, we may wait indefinitely for a frame that will never come.
// If the compositor is not visible, we will not get a frame, so don't wait.
if (!needs_frame || !compositor()->IsVisible()) {
return -1;
}
// Update window()'s LocalSurfaceId. This will ensure that the parent ID is
// updated both here and for LayerTreeHostImpl. So, the CompositorFrame sent
// by LayerTreeHostImpl will include the updated parent ID for
// synchronization. Some operations may have already updated the
// LocalSurfaceId, but this only modifies pending commit state, so it's not
// expensive.
window()->AllocateLocalSurfaceId();
compositor()->SetLocalSurfaceIdFromParent(window()->GetLocalSurfaceId());
return window()->GetLocalSurfaceId().parent_sequence_number();
}
} // namespace aura