Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(400)

Side by Side Diff: ash/utility/partial_screenshot_controller.cc

Issue 1909873002: Allow taking screen shot of a non child window which has delegate to paint. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
5 #include "ash/utility/partial_screenshot_controller.h"
6
7 #include <cmath>
8
9 #include "ash/display/mouse_cursor_event_filter.h"
10 #include "ash/screenshot_delegate.h"
11 #include "ash/shell.h"
12 #include "ash/shell_window_ids.h"
13 #include "base/stl_util.h"
14 #include "ui/compositor/paint_recorder.h"
15 #include "ui/display/screen.h"
16 #include "ui/events/event.h"
17 #include "ui/events/event_handler.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/wm/core/cursor_manager.h"
20
21 namespace ash {
22
23 namespace {
24
25 // The size to increase the invalidated area in the layer to repaint. The area
26 // should be slightly bigger than the actual region because the region indicator
27 // rectangles are drawn outside of the selected region.
28 const int kInvalidateRegionAdditionalSize = 3;
29
30 // This will prevent the user from taking a screenshot across multiple
31 // monitors. it will stop the mouse at the any edge of the screen. must
32 // swtich back on when the screenshot is complete.
33 void EnableMouseWarp(bool enable) {
34 Shell::GetInstance()->mouse_cursor_filter()->set_mouse_warp_enabled(enable);
35 }
36
37 } // namespace
38
39 class PartialScreenshotController::PartialScreenshotLayer
40 : public ui::LayerOwner,
41 public ui::LayerDelegate {
42 public:
43 PartialScreenshotLayer(ui::Layer* parent) {
44 SetLayer(new ui::Layer(ui::LAYER_TEXTURED));
45 layer()->SetFillsBoundsOpaquely(false);
46 layer()->SetBounds(parent->bounds());
47 parent->Add(layer());
48 parent->StackAtTop(layer());
49 layer()->SetVisible(true);
50 layer()->set_delegate(this);
51 }
52 ~PartialScreenshotLayer() override {}
53
54 const gfx::Rect& region() const { return region_; }
55
56 void SetRegion(const gfx::Rect& region) {
57 // Invalidates the region which covers the current and new region.
58 gfx::Rect union_rect(region_);
59 union_rect.Union(region);
60 union_rect.Inset(-kInvalidateRegionAdditionalSize,
61 -kInvalidateRegionAdditionalSize);
62 union_rect.Intersects(layer()->bounds());
63
64 region_ = region;
65 layer()->SchedulePaint(union_rect);
66 }
67
68 private:
69 // ui::LayerDelegate:
70 void OnPaintLayer(const ui::PaintContext& context) override {
71 if (region_.IsEmpty())
72 return;
73
74 // Screenshot area representation: black rectangle with white
75 // rectangle inside. To avoid capturing these rectangles when mouse
76 // release, they should be outside of the actual capturing area.
77 gfx::Rect rect(region_);
78 ui::PaintRecorder recorder(context, layer()->size());
79 rect.Inset(-1, -1);
80 recorder.canvas()->DrawRect(rect, SK_ColorWHITE);
81 rect.Inset(-1, -1);
82 recorder.canvas()->DrawRect(rect, SK_ColorBLACK);
83 }
84
85 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
86
87 void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
88
89 base::Closure PrepareForLayerBoundsChange() override {
90 return base::Closure();
91 }
92
93 gfx::Rect region_;
94
95 DISALLOW_COPY_AND_ASSIGN(PartialScreenshotLayer);
96 };
97
98 class PartialScreenshotController::ScopedCursorSetter {
99 public:
100 ScopedCursorSetter(::wm::CursorManager* cursor_manager,
101 gfx::NativeCursor cursor)
102 : cursor_manager_(nullptr) {
103 if (cursor_manager->IsCursorLocked())
104 return;
105 gfx::NativeCursor original_cursor = cursor_manager->GetCursor();
106 cursor_manager_ = cursor_manager;
107 cursor_manager_->SetCursor(cursor);
108 if (!cursor_manager_->IsCursorVisible())
109 cursor_manager_->ShowCursor();
110 cursor_manager_->LockCursor();
111 // SetCursor does not make any effects at this point but it sets back to
112 // the original cursor when unlocked.
113 cursor_manager_->SetCursor(original_cursor);
114 }
115
116 ~ScopedCursorSetter() {
117 if (cursor_manager_)
118 cursor_manager_->UnlockCursor();
119 }
120
121 private:
122 ::wm::CursorManager* cursor_manager_;
123
124 DISALLOW_COPY_AND_ASSIGN(ScopedCursorSetter);
125 };
126
127 PartialScreenshotController::PartialScreenshotController()
128 : root_window_(nullptr), screenshot_delegate_(nullptr) {
129 // Keep this here and don't move it to StartPartialScreenshotSession(), as it
130 // needs to be pre-pended by MouseCursorEventFilter in Shell::Init().
131 Shell::GetInstance()->PrependPreTargetHandler(this);
132 }
133
134 PartialScreenshotController::~PartialScreenshotController() {
135 if (screenshot_delegate_)
136 Cancel();
137 Shell::GetInstance()->RemovePreTargetHandler(this);
138 }
139
140 void PartialScreenshotController::StartPartialScreenshotSession(
141 ScreenshotDelegate* screenshot_delegate) {
142 // Already in a screenshot session.
143 if (screenshot_delegate_) {
144 DCHECK_EQ(screenshot_delegate_, screenshot_delegate);
145 return;
146 }
147
148 screenshot_delegate_ = screenshot_delegate;
149 display::Screen::GetScreen()->AddObserver(this);
150 for (aura::Window* root : Shell::GetAllRootWindows()) {
151 layers_[root] = new PartialScreenshotLayer(
152 Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer());
153 }
154
155 cursor_setter_.reset(new ScopedCursorSetter(
156 Shell::GetInstance()->cursor_manager(), ui::kCursorCross));
157
158 EnableMouseWarp(false);
159 }
160
161 void PartialScreenshotController::MaybeStart(const ui::LocatedEvent& event) {
162 aura::Window* current_root =
163 static_cast<aura::Window*>(event.target())->GetRootWindow();
164 if (root_window_) {
165 // It's already started. This can happen when the second finger touches
166 // the screen, or combination of the touch and mouse. We should grab the
167 // partial screenshot instead of restarting.
168 if (current_root == root_window_) {
169 Update(event);
170 Complete();
171 }
172 } else {
173 root_window_ = current_root;
174 start_position_ = event.root_location();
175 }
176 }
177
178 void PartialScreenshotController::Complete() {
179 if (!root_window_) {
180 // If we received a released event before we ever got a pressed event
181 // (resulting in setting |root_window_|), we just return without canceling
182 // to keep the screenshot session active waiting for the next press.
183 //
184 // This is to avoid a crash that used to happen when we start the screenshot
185 // session while the mouse is pressed and then release without moving the
186 // mouse. crbug.com/581432.
187 return;
188 }
189
190 DCHECK(layers_.count(root_window_));
191 const gfx::Rect& region = layers_.at(root_window_)->region();
192 if (!region.IsEmpty()) {
193 screenshot_delegate_->HandleTakePartialScreenshot(
194 root_window_, gfx::IntersectRects(root_window_->bounds(), region));
195 }
196 Cancel();
197 }
198
199 void PartialScreenshotController::Cancel() {
200 root_window_ = nullptr;
201 screenshot_delegate_ = nullptr;
202 display::Screen::GetScreen()->RemoveObserver(this);
203 STLDeleteValues(&layers_);
204 cursor_setter_.reset();
205 EnableMouseWarp(true);
206 }
207
208 void PartialScreenshotController::Update(const ui::LocatedEvent& event) {
209 // Update may happen without MaybeStart() if the partial screenshot session
210 // starts when dragging.
211 if (!root_window_)
212 MaybeStart(event);
213
214 DCHECK(layers_.find(root_window_) != layers_.end());
215 layers_.at(root_window_)
216 ->SetRegion(
217 gfx::Rect(std::min(start_position_.x(), event.root_location().x()),
218 std::min(start_position_.y(), event.root_location().y()),
219 ::abs(start_position_.x() - event.root_location().x()),
220 ::abs(start_position_.y() - event.root_location().y())));
221 }
222
223 void PartialScreenshotController::OnKeyEvent(ui::KeyEvent* event) {
224 if (!screenshot_delegate_)
225 return;
226 if (event->type() == ui::ET_KEY_RELEASED &&
227 event->key_code() == ui::VKEY_ESCAPE) {
228 Cancel();
229 }
230
231 // Intercepts all key events.
232 event->StopPropagation();
233 }
234
235 void PartialScreenshotController::OnMouseEvent(ui::MouseEvent* event) {
236 if (!screenshot_delegate_)
237 return;
238 switch (event->type()) {
239 case ui::ET_MOUSE_PRESSED:
240 MaybeStart(*event);
241 break;
242 case ui::ET_MOUSE_DRAGGED:
243 Update(*event);
244 break;
245 case ui::ET_MOUSE_RELEASED:
246 Complete();
247 break;
248 default:
249 // Do nothing.
250 break;
251 }
252 event->StopPropagation();
253 }
254
255 void PartialScreenshotController::OnTouchEvent(ui::TouchEvent* event) {
256 if (!screenshot_delegate_)
257 return;
258 switch (event->type()) {
259 case ui::ET_TOUCH_PRESSED:
260 MaybeStart(*event);
261 break;
262 case ui::ET_TOUCH_MOVED:
263 Update(*event);
264 break;
265 case ui::ET_TOUCH_RELEASED:
266 Complete();
267 break;
268 default:
269 // Do nothing.
270 break;
271 }
272 event->StopPropagation();
273 }
274
275 void PartialScreenshotController::OnDisplayAdded(
276 const display::Display& new_display) {
277 if (!screenshot_delegate_)
278 return;
279 Cancel();
280 }
281
282 void PartialScreenshotController::OnDisplayRemoved(
283 const display::Display& old_display) {
284 if (!screenshot_delegate_)
285 return;
286 Cancel();
287 }
288
289 void PartialScreenshotController::OnDisplayMetricsChanged(
290 const display::Display& display,
291 uint32_t changed_metrics) {}
292
293 } // namespace ash
OLDNEW
« no previous file with comments | « ash/utility/partial_screenshot_controller.h ('k') | ash/utility/partial_screenshot_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698