// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/exo/shell_surface.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/window_state_type.h" #include "ash/wm/window_resizer.h" #include "ash/wm/window_state.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "components/exo/wm_helper.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/window.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/window_util.h" namespace exo { namespace { // Maximum amount of time to wait for contents after a change to maximize, // fullscreen or pinned state. constexpr int kMaximizedOrFullscreenOrPinnedLockTimeoutMs = 100; } // namespace //////////////////////////////////////////////////////////////////////////////// // ShellSurface, ScopedAnimationsDisabled: // Helper class used to temporarily disable animations. Restores the // animations disabled property when instance is destroyed. class ShellSurface::ScopedAnimationsDisabled { public: explicit ScopedAnimationsDisabled(ShellSurface* shell_surface); ~ScopedAnimationsDisabled(); private: ShellSurface* const shell_surface_; bool saved_animations_disabled_ = false; DISALLOW_COPY_AND_ASSIGN(ScopedAnimationsDisabled); }; ShellSurface::ScopedAnimationsDisabled::ScopedAnimationsDisabled( ShellSurface* shell_surface) : shell_surface_(shell_surface) { if (shell_surface_->widget_) { aura::Window* window = shell_surface_->widget_->GetNativeWindow(); saved_animations_disabled_ = window->GetProperty(aura::client::kAnimationsDisabledKey); window->SetProperty(aura::client::kAnimationsDisabledKey, true); } } ShellSurface::ScopedAnimationsDisabled::~ScopedAnimationsDisabled() { if (shell_surface_->widget_) { aura::Window* window = shell_surface_->widget_->GetNativeWindow(); DCHECK_EQ(window->GetProperty(aura::client::kAnimationsDisabledKey), true); window->SetProperty(aura::client::kAnimationsDisabledKey, saved_animations_disabled_); } } //////////////////////////////////////////////////////////////////////////////// // ShellSurface, public: ShellSurface::ShellSurface(Surface* surface, const gfx::Point& origin, bool activatable, bool can_minimize, int container) : ShellSurfaceBase(surface, origin, activatable, can_minimize, container) {} ShellSurface::ShellSurface(Surface* surface) : ShellSurfaceBase(surface, gfx::Point(), true, true, ash::kShellWindowId_DefaultContainer) {} ShellSurface::~ShellSurface() { if (widget_) ash::wm::GetWindowState(widget_->GetNativeWindow())->RemoveObserver(this); } void ShellSurface::SetParent(ShellSurface* parent) { TRACE_EVENT1("exo", "ShellSurface::SetParent", "parent", parent ? base::UTF16ToASCII(parent->title_) : "null"); SetParentWindow(parent ? parent->GetWidget()->GetNativeWindow() : nullptr); } void ShellSurface::Maximize() { TRACE_EVENT0("exo", "ShellSurface::Maximize"); if (!widget_) CreateShellSurfaceWidget(ui::SHOW_STATE_MAXIMIZED); // Note: This will ask client to configure its surface even if already // maximized. ScopedConfigure scoped_configure(this, true); widget_->Maximize(); } void ShellSurface::Minimize() { TRACE_EVENT0("exo", "ShellSurface::Minimize"); if (!widget_) CreateShellSurfaceWidget(ui::SHOW_STATE_MINIMIZED); // Note: This will ask client to configure its surface even if already // minimized. ScopedConfigure scoped_configure(this, true); widget_->Minimize(); } void ShellSurface::Restore() { TRACE_EVENT0("exo", "ShellSurface::Restore"); if (!widget_) return; // Note: This will ask client to configure its surface even if not already // maximized or minimized. ScopedConfigure scoped_configure(this, true); widget_->Restore(); } void ShellSurface::SetFullscreen(bool fullscreen) { TRACE_EVENT1("exo", "ShellSurface::SetFullscreen", "fullscreen", fullscreen); if (!widget_) CreateShellSurfaceWidget(ui::SHOW_STATE_FULLSCREEN); // Note: This will ask client to configure its surface even if fullscreen // state doesn't change. ScopedConfigure scoped_configure(this, true); widget_->SetFullscreen(fullscreen); } void ShellSurface::Resize(int component) { TRACE_EVENT1("exo", "ShellSurface::Resize", "component", component); if (!widget_) return; AttemptToStartDrag(component); } void ShellSurface::InitializeWindowState(ash::wm::WindowState* window_state) { window_state->AddObserver(this); window_state->set_allow_set_bounds_direct(false); widget_->set_movement_disabled(movement_disabled_); window_state->set_ignore_keyboard_bounds_change(movement_disabled_); } //////////////////////////////////////////////////////////////////////////////// // ash::wm::WindowStateObserver overrides: void ShellSurface::OnPreWindowStateTypeChange( ash::wm::WindowState* window_state, ash::mojom::WindowStateType old_type) { ash::mojom::WindowStateType new_type = window_state->GetStateType(); if (ash::IsMinimizedWindowStateType(old_type) || ash::IsMinimizedWindowStateType(new_type)) { return; } if (ash::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) || ash::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) { if (!widget_) return; // When transitioning in/out of maximized or fullscreen mode, we need to // make sure we have a configure callback before we allow the default // cross-fade animations. The configure callback provides a mechanism for // the client to inform us that a frame has taken the state change into // account, and without this cross-fade animations are unreliable. if (!configure_callback_.is_null()) { // Give client a chance to produce a frame that takes state change into // account by acquiring a compositor lock. ui::Compositor* compositor = widget_->GetNativeWindow()->layer()->GetCompositor(); configure_compositor_lock_ = compositor->GetCompositorLock( nullptr, base::TimeDelta::FromMilliseconds( kMaximizedOrFullscreenOrPinnedLockTimeoutMs)); } else { scoped_animations_disabled_ = std::make_unique(this); } } } void ShellSurface::OnPostWindowStateTypeChange( ash::wm::WindowState* window_state, ash::mojom::WindowStateType old_type) { ash::mojom::WindowStateType new_type = window_state->GetStateType(); if (ash::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) { Configure(); } if (widget_) { UpdateWidgetBounds(); UpdateShadow(); } // Re-enable animations if they were disabled in pre state change handler. scoped_animations_disabled_.reset(); } } // namespace exo