#include "ui/wm/core/cursor_manager.h"
#include <utility>
#include "base/check_op.h"
#include "base/observer_list.h"
#include "base/trace_event/trace_event.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/client/cursor_client_observer.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/cursor_size.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/ui_base_features.h"
#include "ui/wm/core/cursor_util.h"
#include "ui/wm/core/native_cursor_manager.h"
#include "ui/wm/core/native_cursor_manager_delegate.h"
namespace wm {
namespace internal {
class CursorState {
public:
CursorState() = default;
CursorState(const CursorState&) = delete;
CursorState& operator=(const CursorState&) = delete;
gfx::NativeCursor cursor() const { return cursor_; }
void set_cursor(gfx::NativeCursor cursor) { cursor_ = cursor; }
bool visible() const { return visible_; }
void SetVisible(bool visible) {
if (mouse_events_enabled_)
visible_ = visible;
}
ui::CursorSize cursor_size() const { return cursor_size_; }
void set_cursor_size(ui::CursorSize cursor_size) {
cursor_size_ = cursor_size;
}
int large_cursor_size_in_dip() const { return large_cursor_size_in_dip_; }
void set_large_cursor_size_in_dip(int large_cursor_size_in_dip) {
large_cursor_size_in_dip_ = large_cursor_size_in_dip;
}
SkColor cursor_color() const { return cursor_color_; }
void set_cursor_color(SkColor cursor_color) { cursor_color_ = cursor_color; }
const gfx::Size& system_cursor_size() const { return system_cursor_size_; }
void set_system_cursor_size(const gfx::Size& system_cursor_size) {
system_cursor_size_ = system_cursor_size;
}
bool mouse_events_enabled() const { return mouse_events_enabled_; }
void SetMouseEventsEnabled(bool enabled) {
if (mouse_events_enabled_ == enabled)
return;
mouse_events_enabled_ = enabled;
if (enabled) {
visible_ = visible_on_mouse_events_enabled_;
} else {
visible_on_mouse_events_enabled_ = visible_;
visible_ = false;
}
}
private:
gfx::NativeCursor cursor_;
bool visible_ = true;
ui::CursorSize cursor_size_ = ui::CursorSize::kNormal;
int large_cursor_size_in_dip_ = ui::kDefaultLargeCursorSize;
SkColor cursor_color_ = ui::kDefaultCursorColor;
bool mouse_events_enabled_ = true;
bool visible_on_mouse_events_enabled_ = true;
gfx::Size system_cursor_size_;
};
}
bool CursorManager::last_cursor_visibility_state_ = true;
CursorManager::CursorManager(std::unique_ptr<NativeCursorManager> delegate)
: delegate_(std::move(delegate)),
cursor_lock_count_(0),
current_state_(new internal::CursorState),
state_on_unlock_(new internal::CursorState) {
current_state_->SetVisible(last_cursor_visibility_state_);
}
CursorManager::~CursorManager() {
}
void CursorManager::ResetCursorVisibilityStateForTest() {
last_cursor_visibility_state_ = true;
}
void CursorManager::SetCursor(gfx::NativeCursor cursor) {
SetCursorImpl(cursor, false);
}
gfx::NativeCursor CursorManager::GetCursor() const {
return current_state_->cursor();
}
void CursorManager::SetCursorForced(gfx::NativeCursor cursor) {
SetCursorImpl(cursor, true);
}
void CursorManager::ShowCursor() {
last_cursor_visibility_state_ = true;
state_on_unlock_->SetVisible(true);
if (cursor_lock_count_ == 0 &&
IsCursorVisible() != state_on_unlock_->visible()) {
delegate_->SetVisibility(state_on_unlock_->visible(), this);
if (GetCursor().type() != ui::mojom::CursorType::kNone) {
observers_.Notify(
&aura::client::CursorClientObserver::OnCursorVisibilityChanged, true);
}
}
}
void CursorManager::HideCursor() {
last_cursor_visibility_state_ = false;
state_on_unlock_->SetVisible(false);
if (cursor_lock_count_ == 0 &&
IsCursorVisible() != state_on_unlock_->visible()) {
delegate_->SetVisibility(state_on_unlock_->visible(), this);
observers_.Notify(
&aura::client::CursorClientObserver::OnCursorVisibilityChanged, false);
}
}
bool CursorManager::IsCursorVisible() const {
return current_state_->visible();
}
void CursorManager::SetCursorSize(ui::CursorSize cursor_size) {
state_on_unlock_->set_cursor_size(cursor_size);
if (GetCursorSize() != state_on_unlock_->cursor_size()) {
delegate_->SetCursorSize(state_on_unlock_->cursor_size(), this);
observers_.Notify(&aura::client::CursorClientObserver::OnCursorSizeChanged,
cursor_size);
}
}
ui::CursorSize CursorManager::GetCursorSize() const {
return current_state_->cursor_size();
}
void CursorManager::SetLargeCursorSizeInDip(int large_cursor_size_in_dip) {
large_cursor_size_in_dip =
std::clamp(large_cursor_size_in_dip, ui::kMinLargeCursorSize,
ui::kMaxLargeCursorSize);
state_on_unlock_->set_large_cursor_size_in_dip(large_cursor_size_in_dip);
if (GetLargeCursorSizeInDip() !=
state_on_unlock_->large_cursor_size_in_dip()) {
delegate_->SetLargeCursorSizeInDip(
state_on_unlock_->large_cursor_size_in_dip(), this);
}
}
int CursorManager::GetLargeCursorSizeInDip() const {
return current_state_->large_cursor_size_in_dip();
}
void CursorManager::SetCursorColor(SkColor color) {
state_on_unlock_->set_cursor_color(color);
if (GetCursorColor() != state_on_unlock_->cursor_color()) {
delegate_->SetCursorColor(state_on_unlock_->cursor_color(), this);
}
}
SkColor CursorManager::GetCursorColor() const {
return current_state_->cursor_color();
}
void CursorManager::EnableMouseEvents() {
TRACE_EVENT0("ui,input", "CursorManager::EnableMouseEvents");
state_on_unlock_->SetMouseEventsEnabled(true);
if (cursor_lock_count_ == 0 &&
IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) {
delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(),
this);
}
}
void CursorManager::DisableMouseEvents() {
TRACE_EVENT0("ui,input", "CursorManager::DisableMouseEvents");
state_on_unlock_->SetMouseEventsEnabled(false);
if (cursor_lock_count_ == 0 &&
IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) {
delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(),
this);
}
}
bool CursorManager::IsMouseEventsEnabled() const {
return current_state_->mouse_events_enabled();
}
void CursorManager::SetDisplay(const display::Display& display) {
display_ = display;
observers_.Notify(&aura::client::CursorClientObserver::OnCursorDisplayChanged,
display);
delegate_->SetDisplay(display, this);
}
const display::Display& CursorManager::GetDisplay() const {
return display_;
}
void CursorManager::LockCursor() {
cursor_lock_count_++;
}
void CursorManager::UnlockCursor() {
cursor_lock_count_--;
DCHECK_GE(cursor_lock_count_, 0);
if (cursor_lock_count_ > 0)
return;
if (GetCursor() != state_on_unlock_->cursor()) {
delegate_->SetCursor(state_on_unlock_->cursor(), this);
}
if (IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) {
delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(),
this);
}
if (IsCursorVisible() != state_on_unlock_->visible()) {
delegate_->SetVisibility(state_on_unlock_->visible(),
this);
}
}
bool CursorManager::IsCursorLocked() const {
return cursor_lock_count_ > 0;
}
void CursorManager::AddObserver(
aura::client::CursorClientObserver* observer) {
observers_.AddObserver(observer);
}
void CursorManager::RemoveObserver(
aura::client::CursorClientObserver* observer) {
observers_.RemoveObserver(observer);
}
bool CursorManager::ShouldHideCursorOnKeyEvent(
const ui::KeyEvent& event) const {
return false;
}
bool CursorManager::ShouldHideCursorOnTouchEvent(
const ui::TouchEvent& event) const {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
return true;
#else
return false;
#endif
}
void CursorManager::CommitCursor(gfx::NativeCursor cursor) {
current_state_->set_cursor(cursor);
}
void CursorManager::CommitVisibility(bool visible) {
observers_.Notify(
&aura::client::CursorClientObserver::OnCursorVisibilityChanged,
GetCursor().type() == ui::mojom::CursorType::kNone ? false : visible);
current_state_->SetVisible(visible);
}
void CursorManager::CommitCursorSize(ui::CursorSize cursor_size) {
current_state_->set_cursor_size(cursor_size);
}
void CursorManager::CommitLargeCursorSizeInDip(int large_cursor_size_in_dip) {
current_state_->set_large_cursor_size_in_dip(large_cursor_size_in_dip);
}
void CursorManager::CommitCursorColor(SkColor color) {
current_state_->set_cursor_color(color);
}
void CursorManager::CommitMouseEventsEnabled(bool enabled) {
current_state_->SetMouseEventsEnabled(enabled);
}
gfx::Size CursorManager::GetSystemCursorSize() const {
return current_state_->system_cursor_size();
}
#if BUILDFLAG(IS_WIN)
void CursorManager::UpdateSystemCursorVisibilityForTest(bool visible) {
UpdateSystemCursorVisibility(visible);
}
#endif
void CursorManager::CommitSystemCursorVisibility(bool visible) {
DCHECK(features::ShouldUseCursorEventHook());
UpdateSystemCursorVisibility(visible);
}
void CursorManager::UpdateSystemCursorVisibility(bool visible) {
if (visible == current_state_->visible()) {
return;
}
if (!visible) {
scoped_cursor_lock_.emplace(this);
} else {
scoped_cursor_lock_.reset();
}
CommitVisibility(visible);
}
void CursorManager::CommitSystemCursorSize(
const gfx::Size& system_cursor_size) {
current_state_->set_system_cursor_size(system_cursor_size);
observers_.Notify(
&aura::client::CursorClientObserver::OnSystemCursorSizeChanged,
system_cursor_size);
}
void CursorManager::SetCursorImpl(gfx::NativeCursor cursor, bool forced) {
bool previously_visible = GetCursor().type() != ui::mojom::CursorType::kNone;
state_on_unlock_->set_cursor(cursor);
if (cursor_lock_count_ == 0 &&
(forced || GetCursor() != state_on_unlock_->cursor())) {
delegate_->SetCursor(state_on_unlock_->cursor(), this);
bool is_visible = cursor.type() != ui::mojom::CursorType::kNone;
if (is_visible != previously_visible) {
observers_.Notify(
&aura::client::CursorClientObserver::OnCursorVisibilityChanged,
is_visible);
}
}
}
CursorManager::ScopedCursorLock::ScopedCursorLock(CursorManager* cursor_manager)
: cursor_manager_(cursor_manager) {
cursor_manager_->LockCursor();
}
CursorManager::ScopedCursorLock::~ScopedCursorLock() {
cursor_manager_->UnlockCursor();
}
}