Update path of checkdeps to buildtools checkout
[chromium-blink-merge.git] / win8 / metro_driver / print_handler.cc
blobc409fcd57ad04d1824ab4cbf9861666f41ab73ac
1 // Copyright (c) 2012 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.
5 #include "stdafx.h"
6 #include "win8/metro_driver/print_handler.h"
8 #include <windows.graphics.display.h>
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "chrome/app/chrome_command_ids.h"
14 #include "win8/metro_driver/chrome_app_view.h"
15 #include "win8/metro_driver/winrt_utils.h"
17 namespace {
19 typedef winfoundtn::ITypedEventHandler<
20 wingfx::Printing::PrintManager*,
21 wingfx::Printing::PrintTaskRequestedEventArgs*> PrintRequestedHandler;
23 typedef winfoundtn::ITypedEventHandler<
24 wingfx::Printing::PrintTask*,
25 wingfx::Printing::PrintTaskCompletedEventArgs*> PrintTaskCompletedHandler;
27 typedef winfoundtn::ITypedEventHandler<
28 wingfx::Printing::PrintTask*, IInspectable*> PrintTaskInspectableHandler;
30 typedef winfoundtn::ITypedEventHandler<
31 wingfx::Printing::PrintTask*,
32 wingfx::Printing::PrintTaskProgressingEventArgs*>
33 PrintTaskProgressingHandler;
35 } // namespace
37 namespace metro_driver {
39 mswr::ComPtr<PrintDocumentSource> PrintHandler::current_document_source_;
40 bool PrintHandler::printing_enabled_ = false;
41 base::Lock* PrintHandler::lock_ = NULL;
42 base::Thread* PrintHandler::thread_ = NULL;
44 PrintHandler::PrintHandler() {
45 DCHECK(lock_ == NULL);
46 lock_ = new base::Lock();
48 DCHECK(thread_ == NULL);
49 thread_ = new base::Thread("Metro Print Handler");
50 thread_->Start();
53 PrintHandler::~PrintHandler() {
54 ClearPrintTask();
55 DCHECK(current_document_source_.Get() == NULL);
57 // Get all pending tasks to complete cleanly by Stopping the thread.
58 // They should complete quickly since current_document_source_ is NULL.
59 DCHECK(thread_ != NULL);
60 DCHECK(thread_->IsRunning());
61 thread_->Stop();
62 delete thread_;
63 thread_ = NULL;
65 DCHECK(lock_ != NULL);
66 delete lock_;
67 lock_ = NULL;
70 HRESULT PrintHandler::Initialize(winui::Core::ICoreWindow* window) {
71 // Register for Print notifications.
72 mswr::ComPtr<wingfx::Printing::IPrintManagerStatic> print_mgr_static;
73 HRESULT hr = winrt_utils::CreateActivationFactory(
74 RuntimeClass_Windows_Graphics_Printing_PrintManager,
75 print_mgr_static.GetAddressOf());
76 if (FAILED(hr)) {
77 LOG(ERROR) << "Failed to create PrintManagerStatic " << std::hex << hr;
78 return hr;
81 mswr::ComPtr<wingfx::Printing::IPrintManager> print_mgr;
82 hr = print_mgr_static->GetForCurrentView(&print_mgr);
83 if (FAILED(hr)) {
84 LOG(ERROR) << "Failed to get PrintManager for current view " << std::hex
85 << hr;
86 return hr;
89 hr = print_mgr->add_PrintTaskRequested(
90 mswr::Callback<PrintRequestedHandler>(
91 this, &PrintHandler::OnPrintRequested).Get(),
92 &print_requested_token_);
93 LOG_IF(ERROR, FAILED(hr)) << "Failed to register PrintTaskRequested "
94 << std::hex << hr;
96 mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties;
97 hr = winrt_utils::CreateActivationFactory(
98 RuntimeClass_Windows_Graphics_Display_DisplayProperties,
99 display_properties.GetAddressOf());
100 if (FAILED(hr)) {
101 LOG(ERROR) << "Failed to create DisplayPropertiesStatics " << std::hex
102 << hr;
103 return hr;
106 hr = display_properties->add_LogicalDpiChanged(
107 mswr::Callback<
108 wingfx::Display::IDisplayPropertiesEventHandler,
109 PrintHandler>(this, &PrintHandler::LogicalDpiChanged).Get(),
110 &dpi_change_token_);
111 LOG_IF(ERROR, FAILED(hr)) << "Failed to register LogicalDpiChanged "
112 << std::hex << hr;
114 // This flag adds support for surfaces with a different color channel
115 // ordering than the API default. It is recommended usage, and is required
116 // for compatibility with Direct2D.
117 UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
118 #if defined(_DEBUG)
119 creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
120 #endif
122 // This array defines the set of DirectX hardware feature levels we support.
123 // The ordering MUST be preserved. All applications are assumed to support
124 // 9.1 unless otherwise stated by the application, which is not our case.
125 D3D_FEATURE_LEVEL feature_levels[] = {
126 D3D_FEATURE_LEVEL_11_1,
127 D3D_FEATURE_LEVEL_11_0,
128 D3D_FEATURE_LEVEL_10_1,
129 D3D_FEATURE_LEVEL_10_0,
130 D3D_FEATURE_LEVEL_9_3,
131 D3D_FEATURE_LEVEL_9_2,
132 D3D_FEATURE_LEVEL_9_1 };
134 mswr::ComPtr<ID3D11Device> device;
135 mswr::ComPtr<ID3D11DeviceContext> context;
136 hr = D3D11CreateDevice(
137 NULL, // Specify null to use the default adapter.
138 D3D_DRIVER_TYPE_HARDWARE,
139 0, // Leave as 0 unless software device.
140 creation_flags,
141 feature_levels,
142 ARRAYSIZE(feature_levels),
143 D3D11_SDK_VERSION, // Must always use this value in Metro apps.
144 &device,
145 NULL, // Returns feature level of device created.
146 &context);
147 if (hr == DXGI_ERROR_UNSUPPORTED) {
148 // The hardware is not supported, try a reference driver instead.
149 hr = D3D11CreateDevice(
150 NULL, // Specify null to use the default adapter.
151 D3D_DRIVER_TYPE_REFERENCE,
152 0, // Leave as 0 unless software device.
153 creation_flags,
154 feature_levels,
155 ARRAYSIZE(feature_levels),
156 D3D11_SDK_VERSION, // Must always use this value in Metro apps.
157 &device,
158 NULL, // Returns feature level of device created.
159 &context);
161 if (FAILED(hr)) {
162 LOG(ERROR) << "Failed to create D3D11 device/context " << std::hex << hr;
163 return hr;
166 hr = device.As(&directx_context_.d3d_device);
167 if (FAILED(hr)) {
168 LOG(ERROR) << "Failed to QI D3D11 device " << std::hex << hr;
169 return hr;
172 D2D1_FACTORY_OPTIONS options;
173 ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
175 #if defined(_DEBUG)
176 options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
177 #endif
179 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
180 __uuidof(ID2D1Factory1),
181 &options,
182 &directx_context_.d2d_factory);
183 if (FAILED(hr)) {
184 LOG(ERROR) << "Failed to create D2D1 factory " << std::hex << hr;
185 return hr;
188 mswr::ComPtr<IDXGIDevice> dxgi_device;
189 hr = directx_context_.d3d_device.As(&dxgi_device);
190 if (FAILED(hr)) {
191 LOG(ERROR) << "Failed to QI for IDXGIDevice " << std::hex << hr;
192 return hr;
195 hr = directx_context_.d2d_factory->CreateDevice(
196 dxgi_device.Get(), &directx_context_.d2d_device);
197 if (FAILED(hr)) {
198 LOG(ERROR) << "Failed to Create D2DDevice " << std::hex << hr;
199 return hr;
202 hr = directx_context_.d2d_device->CreateDeviceContext(
203 D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
204 &directx_context_.d2d_context);
205 if (FAILED(hr)) {
206 LOG(ERROR) << "Failed to Create D2DDeviceContext " << std::hex << hr;
207 return hr;
210 hr = CoCreateInstance(CLSID_WICImagingFactory,
211 NULL,
212 CLSCTX_INPROC_SERVER,
213 IID_PPV_ARGS(&directx_context_.wic_factory));
214 if (FAILED(hr)) {
215 LOG(ERROR) << "Failed to CoCreate WICImagingFactory " << std::hex << hr;
216 return hr;
218 return hr;
221 void PrintHandler::EnablePrinting(bool printing_enabled) {
222 thread_->message_loop()->PostTask(
223 FROM_HERE,
224 base::Bind(&PrintHandler::OnEnablePrinting, printing_enabled));
227 void PrintHandler::SetPageCount(size_t page_count) {
228 thread_->message_loop()->PostTask(
229 FROM_HERE,
230 base::Bind(&PrintHandler::OnSetPageCount, page_count));
233 void PrintHandler::AddPage(size_t page_number, IStream* metafile_stream) {
234 thread_->message_loop()->PostTask(
235 FROM_HERE,
236 base::Bind(&PrintHandler::OnAddPage,
237 page_number,
238 mswr::ComPtr<IStream>(metafile_stream)));
241 void PrintHandler::ShowPrintUI() {
242 // Post the print UI request over to the metro thread.
243 DCHECK(globals.appview_msg_loop != NULL);
244 bool posted = globals.appview_msg_loop->PostTask(
245 FROM_HERE, base::Bind(&metro_driver::PrintHandler::OnShowPrintUI));
246 DCHECK(posted);
249 HRESULT PrintHandler::OnPrintRequested(
250 wingfx::Printing::IPrintManager* print_mgr,
251 wingfx::Printing::IPrintTaskRequestedEventArgs* event_args) {
252 DVLOG(1) << __FUNCTION__;
254 HRESULT hr = S_OK;
255 if (printing_enabled_) {
256 mswr::ComPtr<wingfx::Printing::IPrintTaskRequest> print_request;
257 hr = event_args->get_Request(print_request.GetAddressOf());
258 if (FAILED(hr)) {
259 LOG(ERROR) << "Failed to get the Print Task request " << std::hex << hr;
260 return hr;
263 mswrw::HString title;
264 title.Attach(MakeHString(L"Printing"));
265 hr = print_request->CreatePrintTask(
266 title.Get(),
267 mswr::Callback<
268 wingfx::Printing::IPrintTaskSourceRequestedHandler,
269 PrintHandler>(this, &PrintHandler::OnPrintTaskSourceRequest).Get(),
270 print_task_.GetAddressOf());
271 if (FAILED(hr)) {
272 LOG(ERROR) << "Failed to create the Print Task " << std::hex << hr;
273 return hr;
276 hr = print_task_->add_Completed(
277 mswr::Callback<PrintTaskCompletedHandler>(
278 this, &PrintHandler::OnCompleted).Get(), &print_completed_token_);
279 LOG_IF(ERROR, FAILED(hr)) << "Failed to create the Print Task " << std::hex
280 << hr;
282 return hr;
285 HRESULT PrintHandler::OnPrintTaskSourceRequest(
286 wingfx::Printing::IPrintTaskSourceRequestedArgs* args) {
287 DVLOG(1) << __FUNCTION__;
288 mswr::ComPtr<PrintDocumentSource> print_document_source;
289 HRESULT hr = mswr::MakeAndInitialize<PrintDocumentSource>(
290 &print_document_source, directx_context_, lock_);
291 if (FAILED(hr)) {
292 LOG(ERROR) << "Failed to create document source " << std::hex << hr;
293 return hr;
296 print_document_source->ResetDpi(GetLogicalDpi());
298 mswr::ComPtr<wingfx::Printing::IPrintDocumentSource> print_source;
299 hr = print_document_source.As(&print_source);
300 if (FAILED(hr)) {
301 LOG(ERROR) << "Failed to cast document Source " << std::hex << hr;
302 return hr;
305 hr = args->SetSource(print_source.Get());
306 if (FAILED(hr)) {
307 LOG(ERROR) << "Failed to set document Source " << std::hex << hr;
308 return hr;
311 thread_->message_loop()->PostTask(
312 FROM_HERE,
313 base::Bind(&PrintHandler::SetPrintDocumentSource,
314 print_document_source));
316 return hr;
319 HRESULT PrintHandler::OnCompleted(
320 wingfx::Printing::IPrintTask* task,
321 wingfx::Printing::IPrintTaskCompletedEventArgs* args) {
322 DVLOG(1) << __FUNCTION__;
323 DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
324 ClearPrintTask();
325 thread_->message_loop()->PostTask(
326 FROM_HERE,
327 base::Bind(&PrintHandler::ReleasePrintDocumentSource));
329 return S_OK;
332 void PrintHandler::ClearPrintTask() {
333 if (!print_task_.Get())
334 return;
336 HRESULT hr = print_task_->remove_Completed(print_completed_token_);
337 LOG_IF(ERROR, FAILED(hr)) << "Failed to remove completed event from Task "
338 << std::hex << hr;
339 print_task_.Reset();
342 float PrintHandler::GetLogicalDpi() {
343 mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties;
344 HRESULT hr = winrt_utils::CreateActivationFactory(
345 RuntimeClass_Windows_Graphics_Display_DisplayProperties,
346 display_properties.GetAddressOf());
347 if (FAILED(hr)) {
348 LOG(ERROR) << "Failed to get display properties " << std::hex << hr;
349 return 0.0;
352 FLOAT dpi = 0.0;
353 hr = display_properties->get_LogicalDpi(&dpi);
354 LOG_IF(ERROR, FAILED(hr)) << "Failed to get Logical DPI " << std::hex << hr;
356 return dpi;
359 HRESULT PrintHandler::LogicalDpiChanged(IInspectable *sender) {
360 DVLOG(1) << __FUNCTION__;
361 thread_->message_loop()->PostTask(
362 FROM_HERE,
363 base::Bind(&PrintHandler::OnLogicalDpiChanged, GetLogicalDpi()));
364 return S_OK;
367 void PrintHandler::OnLogicalDpiChanged(float dpi) {
368 DCHECK(base::MessageLoop::current() == thread_->message_loop());
369 // No need to protect the access to the static variable,
370 // since it's set/released in this same thread.
371 if (current_document_source_.Get() != NULL)
372 current_document_source_->ResetDpi(dpi);
375 void PrintHandler::SetPrintDocumentSource(
376 const mswr::ComPtr<PrintDocumentSource>& print_document_source) {
377 DCHECK(base::MessageLoop::current() == thread_->message_loop());
378 DCHECK(current_document_source_.Get() == NULL);
380 // Protect against the other thread which might try to access it.
381 base::AutoLock lock(*lock_);
382 current_document_source_ = print_document_source;
384 // Start generating the images to print.
385 // TODO(mad): Use a registered message with more information about the print
386 // request, and at a more appropriate time too, and maybe one page at a time.
387 ::PostMessageW(globals.host_windows.front().first,
388 WM_SYSCOMMAND,
389 IDC_PRINT_TO_DESTINATION,
393 void PrintHandler::ReleasePrintDocumentSource() {
394 DCHECK(base::MessageLoop::current() == thread_->message_loop());
395 mswr::ComPtr<PrintDocumentSource> print_document_source;
397 // Must wait for other thread to be done with the pointer first.
398 base::AutoLock lock(*lock_);
399 current_document_source_.Swap(print_document_source);
401 // This may happen before we get a chance to set the value.
402 if (print_document_source.Get() != NULL)
403 print_document_source->Abort();
406 void PrintHandler::OnEnablePrinting(bool printing_enabled) {
407 DCHECK(base::MessageLoop::current() == thread_->message_loop());
408 base::AutoLock lock(*lock_);
409 printing_enabled_ = printing_enabled;
410 // Don't abort if we are being disabled since we may be finishing a previous
411 // print request which was valid and should be finished. We just need to
412 // prevent any new print requests. And don't assert that we are NOT printing
413 // if we are becoming enabled since we may be finishing a long print while
414 // we got disabled and then enabled again...
417 void PrintHandler::OnSetPageCount(size_t page_count) {
418 DCHECK(base::MessageLoop::current() == thread_->message_loop());
419 // No need to protect the access to the static variable,
420 // since it's set/released in this same thread.
421 if (current_document_source_.Get() != NULL)
422 current_document_source_->SetPageCount(page_count);
425 void PrintHandler::OnAddPage(size_t page_number,
426 mswr::ComPtr<IStream> metafile_stream) {
427 DCHECK(base::MessageLoop::current() == thread_->message_loop());
428 // No need to protect the access to the static variable,
429 // since it's set/released in this same thread.
430 if (current_document_source_.Get() != NULL)
431 current_document_source_->AddPage(page_number, metafile_stream.Get());
434 void PrintHandler::OnShowPrintUI() {
435 DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
436 mswr::ComPtr<wingfx::Printing::IPrintManagerStatic> print_mgr_static;
437 HRESULT hr = winrt_utils::CreateActivationFactory(
438 RuntimeClass_Windows_Graphics_Printing_PrintManager,
439 print_mgr_static.GetAddressOf());
440 if (SUCCEEDED(hr)) {
441 DCHECK(print_mgr_static.Get() != NULL);
442 // Note that passing NULL to ShowPrintUIAsync crashes,
443 // so we need to setup a temp pointer.
444 mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> unused_async_op;
445 hr = print_mgr_static->ShowPrintUIAsync(unused_async_op.GetAddressOf());
446 LOG_IF(ERROR, FAILED(hr)) << "Failed to ShowPrintUIAsync "
447 << std::hex << std::showbase << hr;
448 } else {
449 LOG(ERROR) << "Failed to create PrintManagerStatic "
450 << std::hex << std::showbase << hr;
454 } // namespace metro_driver
456 void MetroEnablePrinting(BOOL printing_enabled) {
457 metro_driver::PrintHandler::EnablePrinting(!!printing_enabled);
460 void MetroSetPrintPageCount(size_t page_count) {
461 DVLOG(1) << __FUNCTION__ << " Page count: " << page_count;
462 metro_driver::PrintHandler::SetPageCount(page_count);
465 void MetroSetPrintPageContent(size_t page_number,
466 void* data,
467 size_t data_size) {
468 DVLOG(1) << __FUNCTION__ << " Page number: " << page_number;
469 DCHECK(data != NULL);
470 DCHECK(data_size > 0);
471 mswr::ComPtr<IStream> metafile_stream;
472 HRESULT hr = ::CreateStreamOnHGlobal(
473 NULL, TRUE, metafile_stream.GetAddressOf());
474 if (metafile_stream.Get() != NULL) {
475 ULONG bytes_written = 0;
476 hr = metafile_stream->Write(data,
477 base::checked_cast<ULONG>(data_size),
478 &bytes_written);
479 LOG_IF(ERROR, FAILED(hr)) << "Failed to Write to Stream " << std::hex << hr;
480 DCHECK(bytes_written == data_size);
482 metro_driver::PrintHandler::AddPage(page_number, metafile_stream.Get());
483 } else {
484 NOTREACHED() << "Failed to CreateStreamOnHGlobal " << std::hex << hr;
488 void MetroShowPrintUI() {
489 metro_driver::PrintHandler::ShowPrintUI();