Home | History | Annotate | Download | only in metro_driver
      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/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"
     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_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 }
    491