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