Index: win8/metro_driver/print_handler.cc |
diff --git a/win8/metro_driver/print_handler.cc b/win8/metro_driver/print_handler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dce321aedc6ef525506a7c0b6a10ac635713e9ad |
--- /dev/null |
+++ b/win8/metro_driver/print_handler.cc |
@@ -0,0 +1,487 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "stdafx.h" |
+#include "win8/metro_driver/print_handler.h" |
+ |
+#include <windows.graphics.display.h> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "chrome/app/chrome_command_ids.h" |
+#include "win8/metro_driver/chrome_app_view.h" |
+#include "win8/metro_driver/winrt_utils.h" |
+ |
+namespace { |
+ |
+typedef winfoundtn::ITypedEventHandler< |
+ wingfx::Printing::PrintManager*, |
+ wingfx::Printing::PrintTaskRequestedEventArgs*> PrintRequestedHandler; |
+ |
+typedef winfoundtn::ITypedEventHandler< |
+ wingfx::Printing::PrintTask*, |
+ wingfx::Printing::PrintTaskCompletedEventArgs*> PrintTaskCompletedHandler; |
+ |
+typedef winfoundtn::ITypedEventHandler< |
+ wingfx::Printing::PrintTask*, IInspectable*> PrintTaskInspectableHandler; |
+ |
+typedef winfoundtn::ITypedEventHandler< |
+ wingfx::Printing::PrintTask*, |
+ wingfx::Printing::PrintTaskProgressingEventArgs*> |
+ PrintTaskProgressingHandler; |
+ |
+} // namespace |
+ |
+namespace metro_driver { |
+ |
+mswr::ComPtr<PrintDocumentSource> PrintHandler::current_document_source_; |
+bool PrintHandler::printing_enabled_ = false; |
+base::Lock* PrintHandler::lock_ = NULL; |
+base::Thread* PrintHandler::thread_ = NULL; |
+ |
+PrintHandler::PrintHandler() { |
+ DCHECK(lock_ == NULL); |
+ lock_ = new base::Lock(); |
+ |
+ DCHECK(thread_ == NULL); |
+ thread_ = new base::Thread("Metro Print Handler"); |
+ thread_->Start(); |
+} |
+ |
+PrintHandler::~PrintHandler() { |
+ ClearPrintTask(); |
+ DCHECK(current_document_source_.Get() == NULL); |
+ |
+ // Get all pending tasks to complete cleanly by Stopping the thread. |
+ // They should complete quickly since current_document_source_ is NULL. |
+ DCHECK(thread_ != NULL); |
+ DCHECK(thread_->IsRunning()); |
+ thread_->Stop(); |
+ delete thread_; |
+ thread_ = NULL; |
+ |
+ DCHECK(lock_ != NULL); |
+ delete lock_; |
+ lock_ = NULL; |
+} |
+ |
+HRESULT PrintHandler::Initialize(winui::Core::ICoreWindow* window) { |
+ // Register for Print notifications. |
+ mswr::ComPtr<wingfx::Printing::IPrintManagerStatic> print_mgr_static; |
+ HRESULT hr = winrt_utils::CreateActivationFactory( |
+ RuntimeClass_Windows_Graphics_Printing_PrintManager, |
+ print_mgr_static.GetAddressOf()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create PrintManagerStatic " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ mswr::ComPtr<wingfx::Printing::IPrintManager> print_mgr; |
+ hr = print_mgr_static->GetForCurrentView(&print_mgr); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to get PrintManager for current view " << std::hex |
+ << hr; |
+ return hr; |
+ } |
+ |
+ hr = print_mgr->add_PrintTaskRequested( |
+ mswr::Callback<PrintRequestedHandler>( |
+ this, &PrintHandler::OnPrintRequested).Get(), |
+ &print_requested_token_); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to register PrintTaskRequested " |
+ << std::hex << hr; |
+ |
+ mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties; |
+ hr = winrt_utils::CreateActivationFactory( |
+ RuntimeClass_Windows_Graphics_Display_DisplayProperties, |
+ display_properties.GetAddressOf()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create DisplayPropertiesStatics " << std::hex |
+ << hr; |
+ return hr; |
+ } |
+ |
+ hr = display_properties->add_LogicalDpiChanged( |
+ mswr::Callback< |
+ wingfx::Display::IDisplayPropertiesEventHandler, |
+ PrintHandler>(this, &PrintHandler::LogicalDpiChanged).Get(), |
+ &dpi_change_token_); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to register LogicalDpiChanged " |
+ << std::hex << hr; |
+ |
+ // This flag adds support for surfaces with a different color channel |
+ // ordering than the API default. It is recommended usage, and is required |
+ // for compatibility with Direct2D. |
+ UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; |
+#if defined(_DEBUG) |
+ creation_flags |= D3D11_CREATE_DEVICE_DEBUG; |
+#endif |
+ |
+ // This array defines the set of DirectX hardware feature levels we support. |
+ // The ordering MUST be preserved. All applications are assumed to support |
+ // 9.1 unless otherwise stated by the application, which is not our case. |
+ D3D_FEATURE_LEVEL feature_levels[] = { |
+ D3D_FEATURE_LEVEL_11_1, |
+ D3D_FEATURE_LEVEL_11_0, |
+ D3D_FEATURE_LEVEL_10_1, |
+ D3D_FEATURE_LEVEL_10_0, |
+ D3D_FEATURE_LEVEL_9_3, |
+ D3D_FEATURE_LEVEL_9_2, |
+ D3D_FEATURE_LEVEL_9_1 }; |
+ |
+ mswr::ComPtr<ID3D11Device> device; |
+ mswr::ComPtr<ID3D11DeviceContext> context; |
+ hr = D3D11CreateDevice( |
+ NULL, // Specify null to use the default adapter. |
+ D3D_DRIVER_TYPE_HARDWARE, |
+ 0, // Leave as 0 unless software device. |
+ creation_flags, |
+ feature_levels, |
+ ARRAYSIZE(feature_levels), |
+ D3D11_SDK_VERSION, // Must always use this value in Metro apps. |
+ &device, |
+ NULL, // Returns feature level of device created. |
+ &context); |
+ if (hr == DXGI_ERROR_UNSUPPORTED) { |
+ // The hardware is not supported, try a reference driver instead. |
+ hr = D3D11CreateDevice( |
+ NULL, // Specify null to use the default adapter. |
+ D3D_DRIVER_TYPE_REFERENCE, |
+ 0, // Leave as 0 unless software device. |
+ creation_flags, |
+ feature_levels, |
+ ARRAYSIZE(feature_levels), |
+ D3D11_SDK_VERSION, // Must always use this value in Metro apps. |
+ &device, |
+ NULL, // Returns feature level of device created. |
+ &context); |
+ } |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create D3D11 device/context " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ hr = device.As(&directx_context_.d3d_device); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to QI D3D11 device " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ D2D1_FACTORY_OPTIONS options; |
+ ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS)); |
+ |
+#if defined(_DEBUG) |
+ options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; |
+#endif |
+ |
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, |
+ __uuidof(ID2D1Factory1), |
+ &options, |
+ &directx_context_.d2d_factory); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create D2D1 factory " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ mswr::ComPtr<IDXGIDevice> dxgi_device; |
+ hr = directx_context_.d3d_device.As(&dxgi_device); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to QI for IDXGIDevice " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ hr = directx_context_.d2d_factory->CreateDevice( |
+ dxgi_device.Get(), &directx_context_.d2d_device); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to Create D2DDevice " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ hr = directx_context_.d2d_device->CreateDeviceContext( |
+ D2D1_DEVICE_CONTEXT_OPTIONS_NONE, |
+ &directx_context_.d2d_context); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to Create D2DDeviceContext " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ hr = CoCreateInstance(CLSID_WICImagingFactory, |
+ NULL, |
+ CLSCTX_INPROC_SERVER, |
+ IID_PPV_ARGS(&directx_context_.wic_factory)); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to CoCreate WICImagingFactory " << std::hex << hr; |
+ return hr; |
+ } |
+ return hr; |
+} |
+ |
+void PrintHandler::EnablePrinting(bool printing_enabled) { |
+ thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&PrintHandler::OnEnablePrinting, printing_enabled)); |
+} |
+ |
+void PrintHandler::SetPageCount(size_t page_count) { |
+ thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&PrintHandler::OnSetPageCount, page_count)); |
+} |
+ |
+void PrintHandler::AddPage(size_t page_number, IStream* metafile_stream) { |
+ thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&PrintHandler::OnAddPage, |
+ page_number, |
+ mswr::ComPtr<IStream>(metafile_stream))); |
+} |
+ |
+void PrintHandler::ShowPrintUI() { |
+ // Post the print UI request over to the metro thread. |
+ DCHECK(globals.appview_msg_loop != NULL); |
+ bool posted = globals.appview_msg_loop->PostTask( |
+ FROM_HERE, base::Bind(&metro_driver::PrintHandler::OnShowPrintUI)); |
+ DCHECK(posted); |
+} |
+ |
+HRESULT PrintHandler::OnPrintRequested( |
+ wingfx::Printing::IPrintManager* print_mgr, |
+ wingfx::Printing::IPrintTaskRequestedEventArgs* event_args) { |
+ DVLOG(1) << __FUNCTION__; |
+ |
+ HRESULT hr = S_OK; |
+ if (printing_enabled_) { |
+ mswr::ComPtr<wingfx::Printing::IPrintTaskRequest> print_request; |
+ hr = event_args->get_Request(print_request.GetAddressOf()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to get the Print Task request " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ mswrw::HString title; |
+ title.Attach(MakeHString(L"Printing")); |
+ hr = print_request->CreatePrintTask( |
+ title.Get(), |
+ mswr::Callback< |
+ wingfx::Printing::IPrintTaskSourceRequestedHandler, |
+ PrintHandler>(this, &PrintHandler::OnPrintTaskSourceRequest).Get(), |
+ print_task_.GetAddressOf()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create the Print Task " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ hr = print_task_->add_Completed( |
+ mswr::Callback<PrintTaskCompletedHandler>( |
+ this, &PrintHandler::OnCompleted).Get(), &print_completed_token_); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to create the Print Task " << std::hex |
+ << hr; |
+ } |
+ return hr; |
+} |
+ |
+HRESULT PrintHandler::OnPrintTaskSourceRequest( |
+ wingfx::Printing::IPrintTaskSourceRequestedArgs* args) { |
+ DVLOG(1) << __FUNCTION__; |
+ mswr::ComPtr<PrintDocumentSource> print_document_source; |
+ HRESULT hr = mswr::MakeAndInitialize<PrintDocumentSource>( |
+ &print_document_source, directx_context_, lock_); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create document source " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ print_document_source->ResetDpi(GetLogicalDpi()); |
+ |
+ mswr::ComPtr<wingfx::Printing::IPrintDocumentSource> print_source; |
+ hr = print_document_source.As(&print_source); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to cast document Source " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ hr = args->SetSource(print_source.Get()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to set document Source " << std::hex << hr; |
+ return hr; |
+ } |
+ |
+ thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&PrintHandler::SetPrintDocumentSource, |
+ print_document_source)); |
+ |
+ return hr; |
+} |
+ |
+HRESULT PrintHandler::OnCompleted( |
+ wingfx::Printing::IPrintTask* task, |
+ wingfx::Printing::IPrintTaskCompletedEventArgs* args) { |
+ DVLOG(1) << __FUNCTION__; |
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread()); |
+ ClearPrintTask(); |
+ thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&PrintHandler::ReleasePrintDocumentSource)); |
+ |
+ return S_OK; |
+} |
+ |
+void PrintHandler::ClearPrintTask() { |
+ if (!print_task_.Get()) |
+ return; |
+ |
+ HRESULT hr = print_task_->remove_Completed(print_completed_token_); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to remove completed event from Task " |
+ << std::hex << hr; |
+ print_task_.Reset(); |
+} |
+ |
+float PrintHandler::GetLogicalDpi() { |
+ mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties; |
+ HRESULT hr = winrt_utils::CreateActivationFactory( |
+ RuntimeClass_Windows_Graphics_Display_DisplayProperties, |
+ display_properties.GetAddressOf()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to get display properties " << std::hex << hr; |
+ return 0.0; |
+ } |
+ |
+ FLOAT dpi = 0.0; |
+ hr = display_properties->get_LogicalDpi(&dpi); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to get Logical DPI " << std::hex << hr; |
+ |
+ return dpi; |
+} |
+ |
+HRESULT PrintHandler::LogicalDpiChanged(IInspectable *sender) { |
+ DVLOG(1) << __FUNCTION__; |
+ thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&PrintHandler::OnLogicalDpiChanged, GetLogicalDpi())); |
+ return S_OK; |
+} |
+ |
+void PrintHandler::OnLogicalDpiChanged(float dpi) { |
+ DCHECK(MessageLoop::current() == thread_->message_loop()); |
+ // No need to protect the access to the static variable, |
+ // since it's set/released in this same thread. |
+ if (current_document_source_.Get() != NULL) |
+ current_document_source_->ResetDpi(dpi); |
+} |
+ |
+void PrintHandler::SetPrintDocumentSource( |
+ const mswr::ComPtr<PrintDocumentSource>& print_document_source) { |
+ DCHECK(MessageLoop::current() == thread_->message_loop()); |
+ DCHECK(current_document_source_.Get() == NULL); |
+ { |
+ // Protect against the other thread which might try to access it. |
+ base::AutoLock lock(*lock_); |
+ current_document_source_ = print_document_source; |
+ } |
+ // Start generating the images to print. |
+ // TODO(mad): Use a registered message with more information about the print |
+ // request, and at a more appropriate time too, and maybe one page at a time. |
+ ::PostMessageW(globals.host_windows.front().first, |
+ WM_SYSCOMMAND, |
+ IDC_PRINT_TO_DESTINATION, |
+ 0); |
+} |
+ |
+void PrintHandler::ReleasePrintDocumentSource() { |
+ DCHECK(MessageLoop::current() == thread_->message_loop()); |
+ mswr::ComPtr<PrintDocumentSource> print_document_source; |
+ { |
+ // Must wait for other thread to be done with the pointer first. |
+ base::AutoLock lock(*lock_); |
+ current_document_source_.Swap(print_document_source); |
+ } |
+ // This may happen before we get a chance to set the value. |
+ if (print_document_source.Get() != NULL) |
+ print_document_source->Abort(); |
+} |
+ |
+void PrintHandler::OnEnablePrinting(bool printing_enabled) { |
+ DCHECK(MessageLoop::current() == thread_->message_loop()); |
+ base::AutoLock lock(*lock_); |
+ printing_enabled_ = printing_enabled; |
+ // Don't abort if we are being disabled since we may be finishing a previous |
+ // print request which was valid and should be finished. We just need to |
+ // prevent any new print requests. And don't assert that we are NOT printing |
+ // if we are becoming enabled since we may be finishing a long print while |
+ // we got disabled and then enabled again... |
+} |
+ |
+void PrintHandler::OnSetPageCount(size_t page_count) { |
+ DCHECK(MessageLoop::current() == thread_->message_loop()); |
+ // No need to protect the access to the static variable, |
+ // since it's set/released in this same thread. |
+ if (current_document_source_.Get() != NULL) |
+ current_document_source_->SetPageCount(page_count); |
+} |
+ |
+void PrintHandler::OnAddPage(size_t page_number, |
+ mswr::ComPtr<IStream> metafile_stream) { |
+ DCHECK(MessageLoop::current() == thread_->message_loop()); |
+ // No need to protect the access to the static variable, |
+ // since it's set/released in this same thread. |
+ if (current_document_source_.Get() != NULL) |
+ current_document_source_->AddPage(page_number, metafile_stream.Get()); |
+} |
+ |
+void PrintHandler::OnShowPrintUI() { |
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread()); |
+ mswr::ComPtr<wingfx::Printing::IPrintManagerStatic> print_mgr_static; |
+ HRESULT hr = winrt_utils::CreateActivationFactory( |
+ RuntimeClass_Windows_Graphics_Printing_PrintManager, |
+ print_mgr_static.GetAddressOf()); |
+ if (SUCCEEDED(hr)) { |
+ DCHECK(print_mgr_static.Get() != NULL); |
+ // Note that passing NULL to ShowPrintUIAsync crashes, |
+ // so we need to setup a temp pointer. |
+ mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> unused_async_op; |
+ hr = print_mgr_static->ShowPrintUIAsync(unused_async_op.GetAddressOf()); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to ShowPrintUIAsync " |
+ << std::hex << std::showbase << hr; |
+ } else { |
+ LOG(ERROR) << "Failed to create PrintManagerStatic " |
+ << std::hex << std::showbase << hr; |
+ } |
+} |
+ |
+} // namespace metro_driver |
+ |
+void MetroEnablePrinting(BOOL printing_enabled) { |
+ metro_driver::PrintHandler::EnablePrinting(!!printing_enabled); |
+} |
+ |
+void MetroSetPrintPageCount(size_t page_count) { |
+ DVLOG(1) << __FUNCTION__ << " Page count: " << page_count; |
+ metro_driver::PrintHandler::SetPageCount(page_count); |
+} |
+ |
+void MetroSetPrintPageContent(size_t page_number, |
+ void* data, |
+ size_t data_size) { |
+ DVLOG(1) << __FUNCTION__ << " Page number: " << page_number; |
+ DCHECK(data != NULL); |
+ DCHECK(data_size > 0); |
+ mswr::ComPtr<IStream> metafile_stream; |
+ HRESULT hr = ::CreateStreamOnHGlobal( |
+ NULL, TRUE, metafile_stream.GetAddressOf()); |
+ if (metafile_stream.Get() != NULL) { |
+ ULONG bytes_written = 0; |
+ hr = metafile_stream->Write(data, data_size, &bytes_written); |
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to Write to Stream " << std::hex << hr; |
+ DCHECK(bytes_written == data_size); |
+ |
+ metro_driver::PrintHandler::AddPage(page_number, metafile_stream.Get()); |
+ } else { |
+ NOTREACHED() << "Failed to CreateStreamOnHGlobal " << std::hex << hr; |
+ } |
+} |
+ |
+void MetroShowPrintUI() { |
+ metro_driver::PrintHandler::ShowPrintUI(); |
+} |