| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2017 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 |
| 5 #include "content/browser/media/capture/cursor_renderer_mac.h" |
| 6 |
| 7 #include <Cocoa/Cocoa.h> |
| 8 |
| 9 #include "base/mac/scoped_nsobject.h" |
| 10 #include "base/memory/ptr_util.h" |
| 11 #include "base/test/simple_test_tick_clock.h" |
| 12 #include "media/base/video_frame.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 #include "ui/gfx/mac/coordinate_conversion.h" |
| 15 #include "ui/gfx/test/ui_cocoa_test_helper.h" |
| 16 |
| 17 namespace content { |
| 18 |
| 19 const int kTestViewWidth = 800; |
| 20 const int kTestViewHeight = 600; |
| 21 |
| 22 class CursorRendererMacTest : public ui::CocoaTest { |
| 23 public: |
| 24 CursorRendererMacTest() {} |
| 25 ~CursorRendererMacTest() override{}; |
| 26 |
| 27 void SetUp() override { |
| 28 ui::CocoaTest::SetUp(); |
| 29 base::scoped_nsobject<NSView> view([[NSView alloc] |
| 30 initWithFrame:NSMakeRect(0, 0, kTestViewWidth, kTestViewHeight)]); |
| 31 view_ = view.get(); |
| 32 [[test_window() contentView] addSubview:view_]; |
| 33 |
| 34 cursor_renderer_.reset(new CursorRendererMac(view_)); |
| 35 } |
| 36 |
| 37 void TearDown() override { |
| 38 cursor_renderer_.reset(); |
| 39 ui::CocoaTest::TearDown(); |
| 40 } |
| 41 |
| 42 void SetTickClock(base::SimpleTestTickClock* clock) { |
| 43 cursor_renderer_->tick_clock_ = clock; |
| 44 } |
| 45 |
| 46 bool CursorDisplayed() { return cursor_renderer_->cursor_displayed_; } |
| 47 |
| 48 void RenderCursorOnVideoFrame(media::VideoFrame* target) { |
| 49 cursor_renderer_->RenderOnVideoFrame(target); |
| 50 } |
| 51 |
| 52 void SnapshotCursorState(gfx::Rect region_in_frame) { |
| 53 cursor_renderer_->SnapshotCursorState(region_in_frame); |
| 54 } |
| 55 |
| 56 // Here the |point| is in Aura coordinates (the origin (0, 0) is at top-left |
| 57 // of the view). To move the cursor to that point by Quartz Display service, |
| 58 // it needs to be converted into Cocoa coordinates (the origin is at |
| 59 // bottom-left of the main screen) first, and then info Quartz coordinates |
| 60 // (the origin is at top-left of the main display). |
| 61 void MoveMouseCursorWithinWindow(gfx::Point point) { |
| 62 point.set_y(kTestViewHeight - point.y()); |
| 63 CGWarpMouseCursorPosition(gfx::ScreenPointToNSPoint(point)); |
| 64 cursor_renderer_->OnMouseEvent(); |
| 65 } |
| 66 |
| 67 void MoveMouseCursorWithinWindow() { |
| 68 CGWarpMouseCursorPosition( |
| 69 gfx::ScreenPointToNSPoint(gfx::Point(50, kTestViewHeight - 50))); |
| 70 cursor_renderer_->OnMouseEvent(); |
| 71 |
| 72 CGWarpMouseCursorPosition( |
| 73 gfx::ScreenPointToNSPoint(gfx::Point(100, kTestViewHeight - 100))); |
| 74 cursor_renderer_->OnMouseEvent(); |
| 75 } |
| 76 |
| 77 void MoveMouseCursorOutsideWindow() { |
| 78 CGWarpMouseCursorPosition(CGPointMake(1000, 200)); |
| 79 cursor_renderer_->OnMouseEvent(); |
| 80 } |
| 81 |
| 82 // A very simple test of whether there are any non-zero pixels |
| 83 // in the region |rect| within |frame|. |
| 84 bool NonZeroPixelsInRegion(scoped_refptr<media::VideoFrame> frame, |
| 85 gfx::Rect rect) { |
| 86 bool y_found = false, u_found = false, v_found = false; |
| 87 for (int y = rect.y(); y < rect.bottom(); ++y) { |
| 88 uint8_t* yplane = frame->data(media::VideoFrame::kYPlane) + |
| 89 y * frame->row_bytes(media::VideoFrame::kYPlane); |
| 90 uint8_t* uplane = frame->data(media::VideoFrame::kUPlane) + |
| 91 (y / 2) * frame->row_bytes(media::VideoFrame::kUPlane); |
| 92 uint8_t* vplane = frame->data(media::VideoFrame::kVPlane) + |
| 93 (y / 2) * frame->row_bytes(media::VideoFrame::kVPlane); |
| 94 for (int x = rect.x(); x < rect.right(); ++x) { |
| 95 if (yplane[x] != 0) |
| 96 y_found = true; |
| 97 if (uplane[x / 2]) |
| 98 u_found = true; |
| 99 if (vplane[x / 2]) |
| 100 v_found = true; |
| 101 } |
| 102 } |
| 103 return (y_found && u_found && v_found); |
| 104 } |
| 105 |
| 106 protected: |
| 107 NSView* view_; |
| 108 std::unique_ptr<CursorRendererMac> cursor_renderer_; |
| 109 }; |
| 110 |
| 111 TEST_F(CursorRendererMacTest, CursorDuringMouseMovement) { |
| 112 // Keep window activated. |
| 113 [test_window() setPretendIsKeyWindow:YES]; |
| 114 |
| 115 EXPECT_FALSE(CursorDisplayed()); |
| 116 |
| 117 base::SimpleTestTickClock clock; |
| 118 SetTickClock(&clock); |
| 119 |
| 120 // Cursor displayed after mouse movement. |
| 121 MoveMouseCursorWithinWindow(); |
| 122 EXPECT_TRUE(CursorDisplayed()); |
| 123 |
| 124 // Cursor not be displayed after idle period. |
| 125 clock.SetNowTicks(base::TimeTicks::Now()); |
| 126 clock.Advance(base::TimeDelta::FromSeconds(5)); |
| 127 SnapshotCursorState(gfx::Rect(10, 10, 200, 200)); |
| 128 EXPECT_FALSE(CursorDisplayed()); |
| 129 clock.SetNowTicks(base::TimeTicks::Now()); |
| 130 |
| 131 // Cursor displayed with mouse movement following idle period. |
| 132 MoveMouseCursorWithinWindow(); |
| 133 SnapshotCursorState(gfx::Rect(10, 10, 200, 200)); |
| 134 EXPECT_TRUE(CursorDisplayed()); |
| 135 |
| 136 // Cursor not displayed if mouse outside the window |
| 137 MoveMouseCursorOutsideWindow(); |
| 138 SnapshotCursorState(gfx::Rect(10, 10, 200, 200)); |
| 139 EXPECT_FALSE(CursorDisplayed()); |
| 140 } |
| 141 |
| 142 TEST_F(CursorRendererMacTest, CursorOnActiveWindow) { |
| 143 EXPECT_FALSE(CursorDisplayed()); |
| 144 |
| 145 // Cursor displayed after mouse movement. |
| 146 [test_window() setPretendIsKeyWindow:YES]; |
| 147 MoveMouseCursorWithinWindow(); |
| 148 EXPECT_TRUE(CursorDisplayed()); |
| 149 |
| 150 // Cursor not be displayed if window is not activated. |
| 151 [test_window() setPretendIsKeyWindow:NO]; |
| 152 SnapshotCursorState(gfx::Rect(10, 10, 200, 200)); |
| 153 EXPECT_FALSE(CursorDisplayed()); |
| 154 |
| 155 // Cursor is displayed again if window is activated again. |
| 156 [test_window() setPretendIsKeyWindow:YES]; |
| 157 MoveMouseCursorWithinWindow(); |
| 158 SnapshotCursorState(gfx::Rect(10, 10, 200, 200)); |
| 159 EXPECT_TRUE(CursorDisplayed()); |
| 160 } |
| 161 |
| 162 TEST_F(CursorRendererMacTest, CursorRenderedOnFrame) { |
| 163 // Keep window activated. |
| 164 [test_window() setPretendIsKeyWindow:YES]; |
| 165 |
| 166 EXPECT_FALSE(CursorDisplayed()); |
| 167 |
| 168 gfx::Size size(kTestViewWidth, kTestViewHeight); |
| 169 scoped_refptr<media::VideoFrame> frame = |
| 170 media::VideoFrame::CreateZeroInitializedFrame(media::PIXEL_FORMAT_YV12, |
| 171 size, gfx::Rect(size), size, |
| 172 base::TimeDelta()); |
| 173 |
| 174 MoveMouseCursorWithinWindow(gfx::Point(60, 60)); |
| 175 SnapshotCursorState(gfx::Rect(size)); |
| 176 EXPECT_TRUE(CursorDisplayed()); |
| 177 |
| 178 EXPECT_FALSE(NonZeroPixelsInRegion(frame, gfx::Rect(50, 50, 70, 70))); |
| 179 RenderCursorOnVideoFrame(frame.get()); |
| 180 EXPECT_TRUE(NonZeroPixelsInRegion(frame, gfx::Rect(50, 50, 70, 70))); |
| 181 } |
| 182 |
| 183 } // namespace content |
| OLD | NEW |