cursor_window_controller.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/display/cursor_window_controller.h"
  5. #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
  6. #include "ash/capture_mode/capture_mode_camera_controller.h"
  7. #include "ash/capture_mode/capture_mode_controller.h"
  8. #include "ash/capture_mode/capture_mode_session.h"
  9. #include "ash/constants/ash_constants.h"
  10. #include "ash/constants/ash_pref_names.h"
  11. #include "ash/constants/ash_switches.h"
  12. #include "ash/display/display_color_manager.h"
  13. #include "ash/display/mirror_window_controller.h"
  14. #include "ash/display/window_tree_host_manager.h"
  15. #include "ash/fast_ink/cursor/cursor_view.h"
  16. #include "ash/public/cpp/shell_window_ids.h"
  17. #include "ash/root_window_controller.h"
  18. #include "ash/session/session_controller_impl.h"
  19. #include "ash/shell.h"
  20. #include "base/command_line.h"
  21. #include "base/metrics/histogram_macros.h"
  22. #include "components/prefs/pref_service.h"
  23. #include "ui/aura/env.h"
  24. #include "ui/aura/window.h"
  25. #include "ui/aura/window_delegate.h"
  26. #include "ui/aura/window_event_dispatcher.h"
  27. #include "ui/base/hit_test.h"
  28. #include "ui/base/layout.h"
  29. #include "ui/base/resource/resource_bundle.h"
  30. #include "ui/compositor/paint_recorder.h"
  31. #include "ui/display/display.h"
  32. #include "ui/display/manager/display_manager.h"
  33. #include "ui/display/screen.h"
  34. #include "ui/gfx/canvas.h"
  35. #include "ui/gfx/geometry/dip_util.h"
  36. #include "ui/gfx/geometry/point_conversions.h"
  37. #include "ui/gfx/image/image_skia.h"
  38. #include "ui/gfx/image/image_skia_operations.h"
  39. #include "ui/views/widget/widget.h"
  40. #include "ui/wm/core/cursors_aura.h"
  41. namespace ash {
  42. namespace {
  43. const int kMinLargeCursorSize = 25;
  44. const int kMaxLargeCursorSize = 64;
  45. } // namespace
  46. class CursorWindowDelegate : public aura::WindowDelegate {
  47. public:
  48. CursorWindowDelegate() = default;
  49. CursorWindowDelegate(const CursorWindowDelegate&) = delete;
  50. CursorWindowDelegate& operator=(const CursorWindowDelegate&) = delete;
  51. ~CursorWindowDelegate() override = default;
  52. // aura::WindowDelegate overrides:
  53. gfx::Size GetMinimumSize() const override { return size_; }
  54. gfx::Size GetMaximumSize() const override { return size_; }
  55. void OnBoundsChanged(const gfx::Rect& old_bounds,
  56. const gfx::Rect& new_bounds) override {}
  57. gfx::NativeCursor GetCursor(const gfx::Point& point) override {
  58. return gfx::kNullCursor;
  59. }
  60. int GetNonClientComponent(const gfx::Point& point) const override {
  61. return HTNOWHERE;
  62. }
  63. bool ShouldDescendIntoChildForEventHandling(
  64. aura::Window* child,
  65. const gfx::Point& location) override {
  66. return false;
  67. }
  68. bool CanFocus() override { return false; }
  69. void OnCaptureLost() override {}
  70. void OnPaint(const ui::PaintContext& context) override {
  71. // No need to cache the output here, the CursorWindow is not invalidated.
  72. ui::PaintRecorder recorder(context, size_);
  73. recorder.canvas()->DrawImageInt(cursor_image_, 0, 0);
  74. }
  75. void OnDeviceScaleFactorChanged(float old_device_scale_factor,
  76. float new_device_scale_factor) override {}
  77. void OnWindowDestroying(aura::Window* window) override {}
  78. void OnWindowDestroyed(aura::Window* window) override {}
  79. void OnWindowTargetVisibilityChanged(bool visible) override {}
  80. bool HasHitTestMask() const override { return false; }
  81. void GetHitTestMask(SkPath* mask) const override {}
  82. // Sets the cursor image for the |display|'s scale factor.
  83. void SetCursorImage(const gfx::Size& size, const gfx::ImageSkia& image) {
  84. size_ = size;
  85. cursor_image_ = image;
  86. }
  87. const gfx::Size& size() const { return size_; }
  88. const gfx::ImageSkia& cursor_image() const { return cursor_image_; }
  89. private:
  90. gfx::ImageSkia cursor_image_;
  91. gfx::Size size_;
  92. };
  93. CursorWindowController::CursorWindowController()
  94. : delegate_(new CursorWindowDelegate()),
  95. is_cursor_motion_blur_enabled_(
  96. base::CommandLine::ForCurrentProcess()->HasSwitch(
  97. switches::kAshEnableCursorMotionBlur)) {}
  98. CursorWindowController::~CursorWindowController() {
  99. SetContainer(NULL);
  100. }
  101. void CursorWindowController::AddObserver(Observer* observer) {
  102. observers_.AddObserver(observer);
  103. }
  104. void CursorWindowController::RemoveObserver(Observer* observer) {
  105. observers_.RemoveObserver(observer);
  106. }
  107. void CursorWindowController::SetLargeCursorSizeInDip(
  108. int large_cursor_size_in_dip) {
  109. large_cursor_size_in_dip =
  110. std::min(large_cursor_size_in_dip, kMaxLargeCursorSize);
  111. large_cursor_size_in_dip =
  112. std::max(large_cursor_size_in_dip, kMinLargeCursorSize);
  113. if (large_cursor_size_in_dip_ == large_cursor_size_in_dip)
  114. return;
  115. large_cursor_size_in_dip_ = large_cursor_size_in_dip;
  116. if (display_.is_valid())
  117. UpdateCursorImage();
  118. }
  119. void CursorWindowController::SetCursorColor(SkColor cursor_color) {
  120. if (cursor_color_ == cursor_color)
  121. return;
  122. cursor_color_ = cursor_color;
  123. if (display_.is_valid())
  124. UpdateCursorImage();
  125. }
  126. bool CursorWindowController::ShouldEnableCursorCompositing() {
  127. if (is_cursor_motion_blur_enabled_)
  128. return true;
  129. auto* controller = CaptureModeController::Get();
  130. auto* session = controller->capture_mode_session();
  131. if (session && session->is_drag_in_progress()) {
  132. // To ensure the cursor is aligned with the dragged region.
  133. return true;
  134. }
  135. if (controller->camera_controller()->is_drag_in_progress()) {
  136. // To ensure the cursor is aligned with the dragged camera preview.
  137. return true;
  138. }
  139. // During startup, we may not have a preference service yet. We need to check
  140. // display manager state first so that we don't accidentally ignore it while
  141. // early outing when there isn't a PrefService yet.
  142. Shell* shell = Shell::Get();
  143. display::DisplayManager* display_manager = shell->display_manager();
  144. if ((display_manager->IsInSoftwareMirrorMode()) ||
  145. display_manager->IsInUnifiedMode() ||
  146. display_manager->screen_capture_is_active()) {
  147. return true;
  148. }
  149. if (shell->fullscreen_magnifier_controller()->IsEnabled())
  150. return true;
  151. if (cursor_color_ != kDefaultCursorColor)
  152. return true;
  153. PrefService* prefs = shell->session_controller()->GetActivePrefService();
  154. if (!prefs) {
  155. // The active pref service can be null early in startup.
  156. return false;
  157. }
  158. if (prefs->GetBoolean(prefs::kNightLightEnabled)) {
  159. // All or some displays don't support setting a CRTC matrix, which means
  160. // Night Light is using the composited color matrix, and hence software
  161. // cursor should be used.
  162. // TODO(afakhry): Instead of switching to the composited cursor on all
  163. // displays if any of them don't support a CRTC matrix, we should provide
  164. // the functionality to turn on the composited cursor on a per-display basis
  165. // (i.e. use it only on the displays that don't support CRTC matrices).
  166. const DisplayColorManager::DisplayCtmSupport displays_ctm_support =
  167. shell->display_color_manager()->displays_ctm_support();
  168. UMA_HISTOGRAM_ENUMERATION("Ash.NightLight.DisplayCrtcCtmSupport",
  169. displays_ctm_support);
  170. if (displays_ctm_support != DisplayColorManager::DisplayCtmSupport::kAll)
  171. return true;
  172. }
  173. return prefs->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
  174. prefs->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
  175. prefs->GetBoolean(prefs::kDockedMagnifierEnabled);
  176. }
  177. void CursorWindowController::SetCursorCompositingEnabled(bool enabled) {
  178. if (is_cursor_compositing_enabled_ == enabled)
  179. return;
  180. is_cursor_compositing_enabled_ = enabled;
  181. if (display_.is_valid())
  182. UpdateCursorImage();
  183. UpdateContainer();
  184. for (auto& obs : observers_)
  185. obs.OnCursorCompositingStateChanged(is_cursor_compositing_enabled_);
  186. }
  187. void CursorWindowController::UpdateContainer() {
  188. if (is_cursor_compositing_enabled_) {
  189. display::Screen* screen = display::Screen::GetScreen();
  190. display::Display display =
  191. screen->GetDisplayNearestPoint(screen->GetCursorScreenPoint());
  192. DCHECK(display.is_valid());
  193. if (display.is_valid())
  194. SetDisplay(display);
  195. } else {
  196. SetContainer(nullptr);
  197. }
  198. // Updates the hot point based on the current display.
  199. UpdateCursorImage();
  200. }
  201. void CursorWindowController::SetDisplay(const display::Display& display) {
  202. if (!is_cursor_compositing_enabled_)
  203. return;
  204. // TODO(oshima): Do not update the composition cursor when crossing
  205. // display in unified desktop mode for now. crbug.com/517222.
  206. if (Shell::Get()->display_manager()->IsInUnifiedMode() &&
  207. display.id() != display::kUnifiedDisplayId) {
  208. return;
  209. }
  210. display_ = display;
  211. aura::Window* root_window = Shell::GetRootWindowForDisplayId(display.id());
  212. if (!root_window)
  213. return;
  214. SetContainer(RootWindowController::ForWindow(root_window)
  215. ->GetContainer(kShellWindowId_MouseCursorContainer));
  216. SetBoundsInScreenAndRotation(display.bounds(), display.rotation());
  217. // Updates the hot point based on the current display.
  218. UpdateCursorImage();
  219. }
  220. void CursorWindowController::OnDockedMagnifierResizingStateChanged(
  221. bool is_active) {
  222. const int container_id = is_active ? kShellWindowId_DockedMagnifierContainer
  223. : kShellWindowId_MouseCursorContainer;
  224. SetContainer(
  225. RootWindowController::ForWindow(container_)->GetContainer(container_id));
  226. }
  227. void CursorWindowController::UpdateLocation() {
  228. if (!cursor_window_)
  229. return;
  230. gfx::Point point = aura::Env::GetInstance()->last_mouse_location();
  231. point.Offset(-bounds_in_screen_.x(), -bounds_in_screen_.y());
  232. point.Offset(-hot_point_.x(), -hot_point_.y());
  233. gfx::Rect bounds = cursor_window_->bounds();
  234. bounds.set_origin(point);
  235. cursor_window_->SetBounds(bounds);
  236. }
  237. void CursorWindowController::SetCursor(gfx::NativeCursor cursor) {
  238. if (cursor_ == cursor)
  239. return;
  240. cursor_ = cursor;
  241. UpdateCursorImage();
  242. UpdateCursorVisibility();
  243. }
  244. void CursorWindowController::SetCursorSize(ui::CursorSize cursor_size) {
  245. cursor_size_ = cursor_size;
  246. UpdateCursorImage();
  247. }
  248. void CursorWindowController::SetVisibility(bool visible) {
  249. visible_ = visible;
  250. UpdateCursorVisibility();
  251. }
  252. const aura::Window* CursorWindowController::GetContainerForTest() const {
  253. return container_;
  254. }
  255. void CursorWindowController::SetContainer(aura::Window* container) {
  256. if (container_ == container)
  257. return;
  258. container_ = container;
  259. if (!container) {
  260. cursor_window_.reset();
  261. cursor_view_widget_.reset();
  262. return;
  263. }
  264. bounds_in_screen_ = display_.bounds();
  265. rotation_ = display_.rotation();
  266. if (is_cursor_motion_blur_enabled_) {
  267. UpdateCursorView();
  268. } else {
  269. // Reusing the window does not work when the display is disconnected.
  270. // Just creates a new one instead. crbug.com/384218.
  271. cursor_window_ = std::make_unique<aura::Window>(delegate_.get());
  272. cursor_window_->SetTransparent(true);
  273. cursor_window_->Init(ui::LAYER_TEXTURED);
  274. cursor_window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
  275. cursor_window_->set_owned_by_parent(false);
  276. // Call UpdateCursorImage() to figure out |cursor_window_|'s desired size.
  277. UpdateCursorImage();
  278. container->AddChild(cursor_window_.get());
  279. }
  280. UpdateCursorVisibility();
  281. UpdateLocation();
  282. }
  283. void CursorWindowController::SetBoundsInScreenAndRotation(
  284. const gfx::Rect& bounds,
  285. display::Display::Rotation rotation) {
  286. if (bounds == bounds_in_screen_ && rotation == rotation_)
  287. return;
  288. bounds_in_screen_ = bounds;
  289. rotation_ = rotation;
  290. if (cursor_view_widget_)
  291. UpdateCursorView();
  292. UpdateLocation();
  293. }
  294. void CursorWindowController::UpdateCursorImage() {
  295. if (!is_cursor_compositing_enabled_)
  296. return;
  297. // Use the original device scale factor instead of the display's, which
  298. // might have been adjusted for the UI scale.
  299. const float original_scale = Shell::Get()
  300. ->display_manager()
  301. ->GetDisplayInfo(display_.id())
  302. .device_scale_factor();
  303. // And use the nearest resource scale factor.
  304. float cursor_scale = ui::GetScaleForResourceScaleFactor(
  305. ui::GetSupportedResourceScaleFactor(original_scale));
  306. gfx::ImageSkia image;
  307. gfx::Point hot_point_in_physical_pixels;
  308. if (cursor_.type() == ui::mojom::CursorType::kCustom) {
  309. const SkBitmap& bitmap = cursor_.custom_bitmap();
  310. if (bitmap.isNull())
  311. return;
  312. image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
  313. hot_point_in_physical_pixels = cursor_.custom_hotspot();
  314. } else {
  315. int resource_id;
  316. if (!wm::GetCursorDataFor(cursor_size_, cursor_.type(), cursor_scale,
  317. &resource_id, &hot_point_in_physical_pixels)) {
  318. return;
  319. }
  320. image =
  321. *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
  322. }
  323. gfx::ImageSkia resized = image;
  324. // Rescale cursor size. This is used with the combination of accessibility
  325. // large cursor. We don't need to care about the case where cursor
  326. // compositing is disabled as we always use cursor compositing if
  327. // accessibility large cursor is enabled.
  328. if (cursor_size_ == ui::CursorSize::kLarge &&
  329. large_cursor_size_in_dip_ != image.size().width()) {
  330. float rescale = static_cast<float>(large_cursor_size_in_dip_) /
  331. static_cast<float>(image.size().width());
  332. resized = gfx::ImageSkiaOperations::CreateResizedImage(
  333. image, skia::ImageOperations::ResizeMethod::RESIZE_GOOD,
  334. gfx::ScaleToCeiledSize(image.size(), rescale));
  335. hot_point_in_physical_pixels =
  336. gfx::ScaleToCeiledPoint(hot_point_in_physical_pixels, rescale);
  337. }
  338. const gfx::ImageSkiaRep& image_rep = resized.GetRepresentation(cursor_scale);
  339. delegate_->SetCursorImage(resized.size(),
  340. gfx::ImageSkia::CreateFromBitmap(
  341. GetAdjustedBitmap(image_rep), cursor_scale));
  342. // TODO(danakj): Should this be rounded? Or kept as a floating point?
  343. hot_point_ = gfx::ToFlooredPoint(
  344. gfx::ConvertPointToDips(hot_point_in_physical_pixels, cursor_scale));
  345. if (cursor_view_widget_) {
  346. static_cast<cursor::CursorView*>(cursor_view_widget_->GetContentsView())
  347. ->SetCursorImage(delegate_->cursor_image(), delegate_->size(),
  348. hot_point_);
  349. }
  350. if (cursor_window_) {
  351. cursor_window_->SetBounds(gfx::Rect(delegate_->size()));
  352. cursor_window_->SchedulePaintInRect(
  353. gfx::Rect(cursor_window_->bounds().size()));
  354. }
  355. UpdateLocation();
  356. }
  357. void CursorWindowController::UpdateCursorVisibility() {
  358. bool visible = (visible_ && cursor_.type() != ui::mojom::CursorType::kNone);
  359. if (visible) {
  360. if (cursor_view_widget_)
  361. cursor_view_widget_->Show();
  362. if (cursor_window_)
  363. cursor_window_->Show();
  364. } else {
  365. if (cursor_view_widget_)
  366. cursor_view_widget_->Hide();
  367. if (cursor_window_)
  368. cursor_window_->Hide();
  369. }
  370. }
  371. void CursorWindowController::UpdateCursorView() {
  372. cursor_view_widget_ = cursor::CursorView::Create(
  373. aura::Env::GetInstance()->last_mouse_location(),
  374. is_cursor_motion_blur_enabled_, container_);
  375. UpdateCursorImage();
  376. }
  377. const gfx::ImageSkia& CursorWindowController::GetCursorImageForTest() const {
  378. return delegate_->cursor_image();
  379. }
  380. SkBitmap CursorWindowController::GetAdjustedBitmap(
  381. const gfx::ImageSkiaRep& image_rep) const {
  382. SkBitmap bitmap = image_rep.GetBitmap();
  383. if (cursor_color_ == kDefaultCursorColor)
  384. return bitmap;
  385. // Recolor the black and greyscale parts of the image based on
  386. // cursor_color_. Do not recolor pure white or tinted portions of the image,
  387. // this ensures we do not impact the colored portions of cursors or the
  388. // transition between the colored portion and white outline.
  389. // TODO(crbug.com/1085442): Programmatically find a way to recolor the white
  390. // parts in order to draw a black outline, but without impacting cursors
  391. // like noDrop which contained tinted portions. Or, add new assets with
  392. // black and white inverted for easier re-coloring.
  393. SkBitmap recolored;
  394. recolored.allocN32Pixels(bitmap.width(), bitmap.height());
  395. recolored.eraseARGB(0, 0, 0, 0);
  396. SkCanvas canvas(recolored);
  397. canvas.drawImage(bitmap.asImage(), 0, 0);
  398. color_utils::HSL cursor_hsl;
  399. color_utils::SkColorToHSL(cursor_color_, &cursor_hsl);
  400. for (int y = 0; y < bitmap.height(); ++y) {
  401. for (int x = 0; x < bitmap.width(); ++x) {
  402. SkColor color = bitmap.getColor(x, y);
  403. // If the alpha is lower than 1, it's transparent, skip it.
  404. if (SkColorGetA(color) < 1)
  405. continue;
  406. // Convert to HSL: We want to change the hue and saturation, and
  407. // map the lightness from 0-100 to cursor_hsl.l-100. This means that
  408. // things which were black (l=0) become the cursor color lightness, and
  409. // things which were white (l=100) stay white.
  410. color_utils::HSL hsl;
  411. color_utils::SkColorToHSL(color, &hsl);
  412. // If it has color, do not change it.
  413. if (hsl.s > 0.01)
  414. continue;
  415. color_utils::HSL result;
  416. result.h = cursor_hsl.h;
  417. result.s = cursor_hsl.s;
  418. result.l = hsl.l * (1 - cursor_hsl.l) + cursor_hsl.l;
  419. SkPaint paint;
  420. paint.setColor(color_utils::HSLToSkColor(result, SkColorGetA(color)));
  421. canvas.drawRect(SkRect::MakeXYWH(x, y, 1, 1), paint);
  422. }
  423. }
  424. return recolored;
  425. }
  426. } // namespace ash