summaryrefslogtreecommitdiff
path: root/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
blob: 02e8a628a6f14d5472bcd34a03cc6ae2481ee8b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2021 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 "ui/ozone/platform/wayland/host/wayland_cursor_factory.h"

#include <wayland-cursor.h>

#include "base/task/task_runner_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "ui/base/cursor/platform_cursor.h"
#include "ui/ozone/common/bitmap_cursor.h"
#include "ui/ozone/common/bitmap_cursor_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"

namespace ui {

namespace {

wl_cursor_theme* LoadCursorTheme(const std::string& name,
                                 int size,
                                 float scale,
                                 wl_shm* shm) {
  // wl_cursor_theme_load() can return nullptr.  We don't check that here but
  // have to be cautious when we actually load the shape.
  return wl_cursor_theme_load((name.empty() ? nullptr : name.c_str()),
                              size * scale, shm);
}

}  // namespace

WaylandCursorFactory::ThemeData::ThemeData() = default;

WaylandCursorFactory::ThemeData::~ThemeData() = default;

WaylandCursorFactory::WaylandCursorFactory(WaylandConnection* connection)
    : connection_(connection) {
  connection_->SetCursorBufferListener(this);
  ReloadThemeCursors();
}

WaylandCursorFactory::~WaylandCursorFactory() = default;

void WaylandCursorFactory::ObserveThemeChanges() {
  auto* linux_ui = LinuxUi::instance();
  DCHECK(linux_ui);
  cursor_theme_observer_.Observe(linux_ui);
}

scoped_refptr<PlatformCursor> WaylandCursorFactory::GetDefaultCursor(
    mojom::CursorType type) {
  if (current_theme_->cache.count(type) == 0) {
    for (const std::string& name : CursorNamesFromType(type)) {
      wl_cursor* cursor = GetCursorFromTheme(name);
      if (!cursor)
        continue;

      current_theme_->cache[type] =
          base::MakeRefCounted<BitmapCursor>(type, cursor, scale_);
      break;
    }
  }
  if (current_theme_->cache.count(type) == 0)
    current_theme_->cache[type] = nullptr;

  // Fall back to the base class implementation if the theme has't provided
  // a shape for the requested type.
  if (current_theme_->cache[type].get() == nullptr)
    return BitmapCursorFactory::GetDefaultCursor(type);

  return current_theme_->cache[type];
}

void WaylandCursorFactory::SetDeviceScaleFactor(float scale) {
  if (scale_ == scale)
    return;

  scale_ = scale;
  ReloadThemeCursors();
}

wl_cursor* WaylandCursorFactory::GetCursorFromTheme(const std::string& name) {
  // Possible if the theme could not be loaded.
  if (!current_theme_->theme)
    return nullptr;

  return wl_cursor_theme_get_cursor(current_theme_->theme.get(), name.c_str());
}

void WaylandCursorFactory::OnCursorThemeNameChanged(
    const std::string& cursor_theme_name) {
  if (name_ == cursor_theme_name)
    return;

  name_ = cursor_theme_name;
  ReloadThemeCursors();
}

void WaylandCursorFactory::OnCursorThemeSizeChanged(int cursor_theme_size) {
  if (size_ == cursor_theme_size)
    return;

  size_ = cursor_theme_size;
  ReloadThemeCursors();
}

void WaylandCursorFactory::OnCursorBufferAttached(wl_cursor* cursor_data) {
  if (!unloaded_theme_)
    return;
  if (!cursor_data) {
    unloaded_theme_.reset();
    return;
  }
  for (auto& item : current_theme_->cache) {
    if (item.second->platform_data() == cursor_data) {
      // The cursor that has been just attached is from the current theme.  That
      // means that the theme that has been unloaded earlier can now be deleted.
      unloaded_theme_.reset();
      return;
    }
  }
}

void WaylandCursorFactory::ReloadThemeCursors() {
  // If we use any cursor when the theme is reloaded, the one can be only from
  // the theme that is currently used.  As soon as we take the next cursor from
  // the next theme, we will destroy it (see OnCursorBufferAttached() above).
  // If more than one theme has been changed but we didn't take any cursors from
  // them (which is possible if the user played with settings but didn't switch
  // into Chromium), we don't need to track them all.
  if (!unloaded_theme_ && current_theme_ && current_theme_->cache.size() > 0)
    unloaded_theme_ = std::move(current_theme_);

  current_theme_ = std::make_unique<ThemeData>();

  // The task environment is normally not created in tests.  As this factory is
  // part of the platform that is created always and early, posting a task to
  // the pool would fail in many many tests.
  if (!base::ThreadPoolInstance::Get())
    return;

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE,
      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
      base::BindOnce(LoadCursorTheme, name_, size_, scale_,
                     connection_->wayland_buffer_factory()->shm()),
      base::BindOnce(&WaylandCursorFactory::OnThemeLoaded,
                     weak_factory_.GetWeakPtr(), name_, size_));
}

void WaylandCursorFactory::OnThemeLoaded(const std::string& loaded_theme_name,
                                         int loaded_theme_size,
                                         wl_cursor_theme* loaded_theme) {
  if (loaded_theme_name == name_ && loaded_theme_size == size_) {
    // wl_cursor_theme_load() can return nullptr.  We don't check that here but
    // have to be cautious when we actually load the shape.
    current_theme_->theme.reset(loaded_theme);
    current_theme_->cache.clear();
    NotifyObserversOnThemeLoaded();
  }
}

}  // namespace ui