// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_EXO_CLIENT_CONTROLLED_SHELL_SURFACE_H_
#define COMPONENTS_EXO_CLIENT_CONTROLLED_SHELL_SURFACE_H_
#include <memory>
#include <string>
#include "ash/display/screen_orientation_controller.h"
#include "ash/public/cpp/arc_resize_lock_type.h"
#include "ash/wm/client_controlled_state.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "components/exo/client_controlled_accelerators.h"
#include "components/exo/shell_surface_base.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/compositor_lock.h"
namespace ash {
class NonClientFrameViewAsh;
class WideFrameView;
} // namespace ash
namespace chromeos {
class ImmersiveFullscreenController;
} // namespace chromeos
namespace exo {
class Surface;
class ClientControlledAcceleratorTarget;
enum class Orientation { PORTRAIT, LANDSCAPE };
enum class ZoomChange { IN, OUT, RESET };
// This class implements a ShellSurface whose window state and bounds are
// controlled by a remote shell client rather than the window manager. The
// position specified as part of the geometry is relative to the origin of
// the screen coordinate system.
class ClientControlledShellSurface : public ShellSurfaceBase,
public ui::CompositorLockClient {
public:
// TODO(mukai): integrate this with ShellSurfaceBase's callback.
class Delegate {
public:
virtual ~Delegate() = default;
virtual void OnGeometryChanged(const gfx::Rect& geometry) = 0;
virtual void OnStateChanged(chromeos::WindowStateType old_state_type,
chromeos::WindowStateType new_state_type) = 0;
virtual void OnBoundsChanged(chromeos::WindowStateType current_state,
chromeos::WindowStateType requested_state,
int64_t display_id,
const gfx::Rect& bounds_in_display,
bool is_resize,
int bounds_change,
bool is_adjusted_bounds) = 0;
virtual void OnDragStarted(int component) = 0;
virtual void OnDragFinished(int x, int y, bool canceled) = 0;
virtual void OnZoomLevelChanged(ZoomChange zoom_change) = 0;
};
ClientControlledShellSurface(Surface* surface,
bool can_minimize,
int container,
bool default_scale_cancellation,
bool supports_floated_state);
ClientControlledShellSurface(const ClientControlledShellSurface&) = delete;
ClientControlledShellSurface& operator=(const ClientControlledShellSurface&) =
delete;
~ClientControlledShellSurface() override;
Delegate* set_delegate(std::unique_ptr<Delegate> delegate) {
delegate_ = std::move(delegate);
return delegate_.get();
}
void set_server_reparent_window(bool reparent) {
server_reparent_window_ = reparent;
}
// Set bounds in root window coordinates relative to the given display.
void SetBounds(int64_t display_id, const gfx::Rect& bounds);
// Set origin of bounds for surface while preserving the size.
void SetBoundsOrigin(int64_t display_id, const gfx::Point& origin);
// Set size of bounds for surface while preserving the origin.
void SetBoundsSize(const gfx::Size& size);
// Called when the client was maximized.
void SetMaximized();
// Called when the client was minimized.
void SetMinimized();
// Called when the client was restored.
void SetRestored();
// Called when the client changed the fullscreen state. When `fullscreen` is
// true, `display_id` indicates the id of the display where the surface should
// be shown, otherwise it is ignored. When `display::kInvalidDisplayId` is
// specified, the current display may be used.
void SetFullscreen(bool fullscreen, int64_t display_id);
// Returns true if this shell surface is currently being dragged.
bool IsDragging();
// Pin/unpin the surface. Pinned surface cannot be switched to
// other windows unless its explicitly unpinned.
void SetPinned(chromeos::WindowPinType type);
// Sets the surface to be on top of all other windows.
void SetAlwaysOnTop(bool always_on_top);
// Controls the visibility of the system UI when this surface is active.
void SetSystemUiVisibility(bool autohide);
// Set orientation for surface.
void SetOrientation(Orientation orientation);
// Set shadow bounds in surface coordinates. Empty bounds disable the shadow.
void SetShadowBounds(const gfx::Rect& bounds);
// Set the pending scale.
void SetScale(double scale);
// Sends the request to change the zoom level to the client.
void ChangeZoomLevel(ZoomChange change);
// Sends the window state change event to client.
void OnWindowStateChangeEvent(chromeos::WindowStateType old_state,
chromeos::WindowStateType next_state);
// Sends the window bounds change event to client. |display_id| specifies in
// which display the surface should live in. |drag_bounds_change| is
// a masked value of ash::WindowResizer::kBoundsChange_Xxx, and specifies
// how the bounds was changed. The bounds change event may also come from a
// snapped window state change |requested_state|.
void OnBoundsChangeEvent(chromeos::WindowStateType current_state,
chromeos::WindowStateType requested_state,
int64_t display_id,
const gfx::Rect& bounds,
int drag_bounds_change,
bool is_adjusted_bounds);
// Sends the window drag events to client.
void OnDragStarted(int component);
void OnDragFinished(bool cancel, const gfx::PointF& location);
// Starts the drag operation.
void StartDrag(int component, const gfx::PointF& location);
// Set if the surface can be maximzied.
void SetCanMaximize(bool can_maximize);
// Update the auto hide frame state.
void UpdateAutoHideFrame();
// Set the frame button state. The |visible_button_mask| and
// |enabled_button_mask| is a bit mask whose position is defined
// in views::CaptionButtonIcon enum.
void SetFrameButtons(uint32_t frame_visible_button_mask,
uint32_t frame_enabled_button_mask);
// Set the extra title for the surface.
void SetExtraTitle(const std::u16string& extra_title);
// Rebind a surface as the root surface of the shell surface.
void RebindRootSurface(Surface* root_surface,
bool can_minimize,
int container,
bool default_scale_cancellation,
bool supports_floated_state);
// SurfaceTreeHost:
void DidReceiveCompositorFrameAck() override;
// ShellSurfaceBase:
bool IsInputEnabled(Surface* surface) const override;
void OnSetFrame(SurfaceFrameType type) override;
void OnSetFrameColors(SkColor active_color, SkColor inactive_color) override;
void SetSnapPrimary(float snap_ratio) override;
void SetSnapSecondary(float snap_ratio) override;
void SetPip() override;
void UnsetPip() override;
void SetFloatToLocation(
chromeos::FloatStartLocation float_start_location) override;
void OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) override;
// views::WidgetDelegate:
void WindowClosing() override;
bool CanMaximize() const override;
std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
views::Widget* widget) override;
bool ShouldSaveWindowPlacement() const override;
void SaveWindowPlacement(const gfx::Rect& bounds,
ui::WindowShowState show_state) override;
bool GetSavedWindowPlacement(const views::Widget* widget,
gfx::Rect* bounds,
ui::WindowShowState* show_state) const override;
// views::View:
gfx::Size GetMaximumSize() const override;
void OnDeviceScaleFactorChanged(float old_dsf, float new_dsf) override;
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override;
void OnWindowAddedToRootWindow(aura::Window* window) override;
// ui::CompositorLockClient:
void CompositorLockTimedOut() override;
// A factory callback to create ClientControlledState::Delegate.
using DelegateFactoryCallback = base::RepeatingCallback<
std::unique_ptr<ash::ClientControlledState::Delegate>(void)>;
// Set the factory callback for unit test.
static void SetClientControlledStateDelegateFactoryForTest(
const DelegateFactoryCallback& callback);
ash::WideFrameView* wide_frame_for_test() { return wide_frame_.get(); }
// Used to scale incoming coordinates from the client to DP.
float GetClientToDpScale() const;
// Used to scale incoming coordinates from the client to DP before the pending
// scale is committed.
float GetClientToDpPendingScale() const;
// Sets the resize lock type to the surface.
void SetResizeLockType(ash::ArcResizeLockType resize_lock_type);
// Update the resizability based on the resize lock type.
void UpdateResizability() override;
// exo::ShellSurfaceBase
void SetSystemModal(bool system_modal) override;
protected:
// ShellSurfaceBase:
float GetScale() const override;
// SurfaceTreeHost:
float GetScaleFactor() const override;
private:
FRIEND_TEST_ALL_PREFIXES(ClientControlledShellSurfaceTest,
OverlayShadowBounds);
class ScopedSetBoundsLocally;
class ScopedLockedToRoot;
class ScopedDeferWindowStateUpdate;
// ShellSurfaceBase:
void SetWidgetBounds(const gfx::Rect& bounds,
bool adjusted_by_server) override;
gfx::Rect GetVisibleBounds() const override;
gfx::Rect GetShadowBounds() const override;
void InitializeWindowState(ash::WindowState* window_state) override;
std::optional<gfx::Rect> GetWidgetBounds() const override;
gfx::Point GetSurfaceOrigin() const override;
bool OnPreWidgetCommit() override;
void ShowWidget(bool activate) override;
void OnPostWidgetCommit() override;
void OnSurfaceDestroying(Surface* surface) override;
// Update frame status. This may create (or destroy) a wide frame
// that spans the full work area width if the surface didn't cover
// the work area.
void UpdateFrame();
void UpdateCaptionButtonModel();
void UpdateBackdrop();
void UpdateFrameWidth();
void UpdateFrameType() override;
bool GetCanResizeFromSizeConstraints() const override;
void AttemptToStartDrag(int component, const gfx::PointF& location);
// Lock the compositor if it's not already locked, or extends the
// lock timeout if it's already locked.
// TODO(reveman): Remove this when using configure callbacks for orientation.
// crbug.com/765954
void EnsureCompositorIsLockedForOrientationChange();
ash::WindowState* GetWindowState();
ash::NonClientFrameViewAsh* GetFrameView();
const ash::NonClientFrameViewAsh* GetFrameView() const;
void EnsurePendingScale(bool commit_immediately);
gfx::Rect GetClientBoundsForWindowBoundsAndWindowState(
const gfx::Rect& window_bounds,
chromeos::WindowStateType window_state) const;
uint32_t frame_visible_button_mask_ = 0;
uint32_t frame_enabled_button_mask_ = 0;
std::unique_ptr<Delegate> delegate_;
// TODO(reveman): Use configure callbacks for orientation. crbug.com/765954
Orientation pending_orientation_ = Orientation::LANDSCAPE;
Orientation orientation_ = Orientation::LANDSCAPE;
Orientation expected_orientation_ = Orientation::LANDSCAPE;
raw_ptr<ash::ClientControlledState> client_controlled_state_ = nullptr;
chromeos::WindowStateType pending_window_state_ =
chromeos::WindowStateType::kNormal;
bool pending_always_on_top_ = false;
SurfaceFrameType pending_frame_type_ = SurfaceFrameType::NONE;
bool can_maximize_ = true;
std::unique_ptr<chromeos::ImmersiveFullscreenController>
immersive_fullscreen_controller_;
std::unique_ptr<ash::WideFrameView> wide_frame_;
std::unique_ptr<ui::CompositorLock> orientation_compositor_lock_;
// The extra title to be applied when widget is being created.
std::u16string initial_extra_title_ = std::u16string();
bool preserve_widget_bounds_ = false;
// Checking DragDetails is not sufficient to determine if a bounds
// request happened during a drag move or resize. If the window resizer
// requests a bounds update after completing the drag but before the
// drag details are cleaned up, we want to consider that as a regular
// bounds update, not a drag move/resize update.
bool in_drag_ = false;
// N uses older protocol which expects that server will reparent the window.
// TODO(oshima): Remove this once all boards are migrated to P or above.
bool server_reparent_window_ = false;
bool display_rotating_with_pip_ = false;
// True if the window state has changed during the commit.
bool state_changed_ = false;
// When false, the client handles all display scale changes, so the
// buffer should be re-scaled to undo any scaling added by exo so that the
// 1:1 correspondence between the pixels is maintained.
bool use_default_scale_cancellation_ = false;
// Client controlled specific accelerator target.
std::unique_ptr<ClientControlledAcceleratorTarget> accelerator_target_;
ash::ArcResizeLockType pending_resize_lock_type_ =
ash::ArcResizeLockType::NONE;
std::unique_ptr<ScopedDeferWindowStateUpdate>
scoped_defer_window_state_update_;
// True if the window supports the floated state.
bool supports_floated_state_;
};
} // namespace exo
#endif // COMPONENTS_EXO_CLIENT_CONTROLLED_SHELL_SURFACE_H_