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.
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"
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
;
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");
53 PrintHandler::~PrintHandler() {
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());
65 DCHECK(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());
77 LOG(ERROR
) << "Failed to create PrintManagerStatic " << std::hex
<< hr
;
81 mswr::ComPtr
<wingfx::Printing::IPrintManager
> print_mgr
;
82 hr
= print_mgr_static
->GetForCurrentView(&print_mgr
);
84 LOG(ERROR
) << "Failed to get PrintManager for current view " << std::hex
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 "
96 mswr::ComPtr
<wingfx::Display::IDisplayPropertiesStatics
> display_properties
;
97 hr
= winrt_utils::CreateActivationFactory(
98 RuntimeClass_Windows_Graphics_Display_DisplayProperties
,
99 display_properties
.GetAddressOf());
101 LOG(ERROR
) << "Failed to create DisplayPropertiesStatics " << std::hex
106 hr
= display_properties
->add_LogicalDpiChanged(
108 wingfx::Display::IDisplayPropertiesEventHandler
,
109 PrintHandler
>(this, &PrintHandler::LogicalDpiChanged
).Get(),
111 LOG_IF(ERROR
, FAILED(hr
)) << "Failed to register LogicalDpiChanged "
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
;
119 creation_flags
|= D3D11_CREATE_DEVICE_DEBUG
;
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.
142 ARRAYSIZE(feature_levels
),
143 D3D11_SDK_VERSION
, // Must always use this value in Metro apps.
145 NULL
, // Returns feature level of device created.
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.
155 ARRAYSIZE(feature_levels
),
156 D3D11_SDK_VERSION
, // Must always use this value in Metro apps.
158 NULL
, // Returns feature level of device created.
162 LOG(ERROR
) << "Failed to create D3D11 device/context " << std::hex
<< hr
;
166 hr
= device
.As(&directx_context_
.d3d_device
);
168 LOG(ERROR
) << "Failed to QI D3D11 device " << std::hex
<< hr
;
172 D2D1_FACTORY_OPTIONS options
;
173 ZeroMemory(&options
, sizeof(D2D1_FACTORY_OPTIONS
));
176 options
.debugLevel
= D2D1_DEBUG_LEVEL_INFORMATION
;
179 hr
= D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED
,
180 __uuidof(ID2D1Factory1
),
182 &directx_context_
.d2d_factory
);
184 LOG(ERROR
) << "Failed to create D2D1 factory " << std::hex
<< hr
;
188 mswr::ComPtr
<IDXGIDevice
> dxgi_device
;
189 hr
= directx_context_
.d3d_device
.As(&dxgi_device
);
191 LOG(ERROR
) << "Failed to QI for IDXGIDevice " << std::hex
<< hr
;
195 hr
= directx_context_
.d2d_factory
->CreateDevice(
196 dxgi_device
.Get(), &directx_context_
.d2d_device
);
198 LOG(ERROR
) << "Failed to Create D2DDevice " << std::hex
<< hr
;
202 hr
= directx_context_
.d2d_device
->CreateDeviceContext(
203 D2D1_DEVICE_CONTEXT_OPTIONS_NONE
,
204 &directx_context_
.d2d_context
);
206 LOG(ERROR
) << "Failed to Create D2DDeviceContext " << std::hex
<< hr
;
210 hr
= CoCreateInstance(CLSID_WICImagingFactory
,
212 CLSCTX_INPROC_SERVER
,
213 IID_PPV_ARGS(&directx_context_
.wic_factory
));
215 LOG(ERROR
) << "Failed to CoCreate WICImagingFactory " << std::hex
<< hr
;
221 void PrintHandler::EnablePrinting(bool printing_enabled
) {
222 thread_
->message_loop()->PostTask(
224 base::Bind(&PrintHandler::OnEnablePrinting
, printing_enabled
));
227 void PrintHandler::SetPageCount(size_t page_count
) {
228 thread_
->message_loop()->PostTask(
230 base::Bind(&PrintHandler::OnSetPageCount
, page_count
));
233 void PrintHandler::AddPage(size_t page_number
, IStream
* metafile_stream
) {
234 thread_
->message_loop()->PostTask(
236 base::Bind(&PrintHandler::OnAddPage
,
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
));
249 HRESULT
PrintHandler::OnPrintRequested(
250 wingfx::Printing::IPrintManager
* print_mgr
,
251 wingfx::Printing::IPrintTaskRequestedEventArgs
* event_args
) {
252 DVLOG(1) << __FUNCTION__
;
255 if (printing_enabled_
) {
256 mswr::ComPtr
<wingfx::Printing::IPrintTaskRequest
> print_request
;
257 hr
= event_args
->get_Request(print_request
.GetAddressOf());
259 LOG(ERROR
) << "Failed to get the Print Task request " << std::hex
<< hr
;
263 mswrw::HString title
;
264 title
.Attach(MakeHString(L
"Printing"));
265 hr
= print_request
->CreatePrintTask(
268 wingfx::Printing::IPrintTaskSourceRequestedHandler
,
269 PrintHandler
>(this, &PrintHandler::OnPrintTaskSourceRequest
).Get(),
270 print_task_
.GetAddressOf());
272 LOG(ERROR
) << "Failed to create the Print Task " << std::hex
<< 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
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_
);
292 LOG(ERROR
) << "Failed to create document source " << std::hex
<< hr
;
296 print_document_source
->ResetDpi(GetLogicalDpi());
298 mswr::ComPtr
<wingfx::Printing::IPrintDocumentSource
> print_source
;
299 hr
= print_document_source
.As(&print_source
);
301 LOG(ERROR
) << "Failed to cast document Source " << std::hex
<< hr
;
305 hr
= args
->SetSource(print_source
.Get());
307 LOG(ERROR
) << "Failed to set document Source " << std::hex
<< hr
;
311 thread_
->message_loop()->PostTask(
313 base::Bind(&PrintHandler::SetPrintDocumentSource
,
314 print_document_source
));
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());
325 thread_
->message_loop()->PostTask(
327 base::Bind(&PrintHandler::ReleasePrintDocumentSource
));
332 void PrintHandler::ClearPrintTask() {
333 if (!print_task_
.Get())
336 HRESULT hr
= print_task_
->remove_Completed(print_completed_token_
);
337 LOG_IF(ERROR
, FAILED(hr
)) << "Failed to remove completed event from Task "
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());
348 LOG(ERROR
) << "Failed to get display properties " << std::hex
<< hr
;
353 hr
= display_properties
->get_LogicalDpi(&dpi
);
354 LOG_IF(ERROR
, FAILED(hr
)) << "Failed to get Logical DPI " << std::hex
<< hr
;
359 HRESULT
PrintHandler::LogicalDpiChanged(IInspectable
*sender
) {
360 DVLOG(1) << __FUNCTION__
;
361 thread_
->message_loop()->PostTask(
363 base::Bind(&PrintHandler::OnLogicalDpiChanged
, GetLogicalDpi()));
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
,
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());
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
;
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
,
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
),
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());
484 NOTREACHED() << "Failed to CreateStreamOnHGlobal " << std::hex
<< hr
;
488 void MetroShowPrintUI() {
489 metro_driver::PrintHandler::ShowPrintUI();