- add sources.
[platform/framework/web/crosswalk.git] / src / win8 / metro_driver / print_handler.cc
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.
4
5 #include "stdafx.h"
6 #include "win8/metro_driver/print_handler.h"
7
8 #include <windows.graphics.display.h>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/safe_numerics.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"
16
17 namespace {
18
19 typedef winfoundtn::ITypedEventHandler<
20     wingfx::Printing::PrintManager*,
21     wingfx::Printing::PrintTaskRequestedEventArgs*> PrintRequestedHandler;
22
23 typedef winfoundtn::ITypedEventHandler<
24     wingfx::Printing::PrintTask*,
25     wingfx::Printing::PrintTaskCompletedEventArgs*> PrintTaskCompletedHandler;
26
27 typedef winfoundtn::ITypedEventHandler<
28     wingfx::Printing::PrintTask*, IInspectable*> PrintTaskInspectableHandler;
29
30 typedef winfoundtn::ITypedEventHandler<
31     wingfx::Printing::PrintTask*,
32     wingfx::Printing::PrintTaskProgressingEventArgs*>
33     PrintTaskProgressingHandler;
34
35 }  // namespace
36
37 namespace metro_driver {
38
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;
43
44 PrintHandler::PrintHandler() {
45   DCHECK(lock_ == NULL);
46   lock_ = new  base::Lock();
47
48   DCHECK(thread_ == NULL);
49   thread_ = new base::Thread("Metro Print Handler");
50   thread_->Start();
51 }
52
53 PrintHandler::~PrintHandler() {
54   ClearPrintTask();
55   DCHECK(current_document_source_.Get() == NULL);
56
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;
64
65   DCHECK(lock_ != NULL);
66   delete lock_;
67   lock_ = NULL;
68 }
69
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;
79   }
80
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;
87   }
88
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;
95
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;
104   }
105
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;
113
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
121
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 };
133
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);
160   }
161   if (FAILED(hr)) {
162     LOG(ERROR) << "Failed to create D3D11 device/context " << std::hex << hr;
163     return hr;
164   }
165
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;
170   }
171
172   D2D1_FACTORY_OPTIONS options;
173   ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
174
175 #if defined(_DEBUG)
176   options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
177 #endif
178
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;
186   }
187
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;
193   }
194
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;
200   }
201
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;
208   }
209
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;
217   }
218   return hr;
219 }
220
221 void PrintHandler::EnablePrinting(bool printing_enabled) {
222   thread_->message_loop()->PostTask(
223       FROM_HERE,
224       base::Bind(&PrintHandler::OnEnablePrinting, printing_enabled));
225 }
226
227 void PrintHandler::SetPageCount(size_t page_count) {
228   thread_->message_loop()->PostTask(
229       FROM_HERE,
230       base::Bind(&PrintHandler::OnSetPageCount, page_count));
231 }
232
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)));
239 }
240
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);
247 }
248
249 HRESULT PrintHandler::OnPrintRequested(
250     wingfx::Printing::IPrintManager* print_mgr,
251     wingfx::Printing::IPrintTaskRequestedEventArgs* event_args) {
252   DVLOG(1) << __FUNCTION__;
253
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;
261     }
262
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;
274     }
275
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;
281   }
282   return hr;
283 }
284
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;
294   }
295
296   print_document_source->ResetDpi(GetLogicalDpi());
297
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;
303   }
304
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;
309   }
310
311   thread_->message_loop()->PostTask(
312       FROM_HERE,
313       base::Bind(&PrintHandler::SetPrintDocumentSource,
314                  print_document_source));
315
316   return hr;
317 }
318
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));
328
329   return S_OK;
330 }
331
332 void PrintHandler::ClearPrintTask() {
333   if (!print_task_.Get())
334     return;
335
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();
340 }
341
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;
350   }
351
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;
355
356   return dpi;
357 }
358
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;
365 }
366
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);
373 }
374
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);
379   {
380     // Protect against the other thread which might try to access it.
381     base::AutoLock lock(*lock_);
382     current_document_source_ = print_document_source;
383   }
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,
390                  0);
391 }
392
393 void PrintHandler::ReleasePrintDocumentSource() {
394   DCHECK(base::MessageLoop::current() == thread_->message_loop());
395   mswr::ComPtr<PrintDocumentSource> print_document_source;
396   {
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);
400   }
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();
404 }
405
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...
415 }
416
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);
423 }
424
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());
432 }
433
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;
451   }
452 }
453
454 }  // namespace metro_driver
455
456 void MetroEnablePrinting(BOOL printing_enabled) {
457   metro_driver::PrintHandler::EnablePrinting(!!printing_enabled);
458 }
459
460 void MetroSetPrintPageCount(size_t page_count) {
461   DVLOG(1) << __FUNCTION__ << " Page count: " << page_count;
462   metro_driver::PrintHandler::SetPageCount(page_count);
463 }
464
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_numeric_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);
481
482     metro_driver::PrintHandler::AddPage(page_number, metafile_stream.Get());
483   } else {
484     NOTREACHED() << "Failed to CreateStreamOnHGlobal " << std::hex << hr;
485   }
486 }
487
488 void MetroShowPrintUI() {
489   metro_driver::PrintHandler::ShowPrintUI();
490 }