From e12567aded4b35f7748525a91cf1c8677864d5c9 Mon Sep 17 00:00:00 2001 From: brige Date: Wed, 18 Dec 2024 21:19:58 +0800 Subject: [PATCH] add cef native render --- src/CEF/BrowserWindow.h | 4 + src/CEF/BrowserWindowOsrWin.cpp | 11 +- src/CEF/BrowserWindowOsrWin.h | 5 +- src/CEF/BrowserWindowStdWin.cpp | 10 +- src/CEF/BrowserWindowStdWin.h | 16 +- src/CEF/ClientHandler.cpp | 2 +- src/CEF/ClientHandlerOSR.h | 1 + src/CEF/HumanAppContextImpl.cpp | 5 +- src/CEF/HumanAppContextImpl.h | 1 + src/CEF/Main.cpp | 107 ++- src/CEF/MainMessageLoopMultithreadedWin.cpp | 163 ++++ src/CEF/MainMessageLoopMultithreadedWin.h | 55 ++ src/CEF/OsrAccessibilityHelper.cpp | 8 +- src/CEF/OsrAccessibilityHelper.h | 8 - src/CEF/OsrAccessibilityNode.cpp | 169 ++++ src/CEF/OsrAccessibilityNode.h | 116 +++ src/CEF/OsrAccessibilityNodeWin.cpp | 684 +++++++++++++++ src/CEF/OsrD3D11Win.cpp | 926 ++++++++++++++++++++ src/CEF/OsrD3D11Win.h | 322 +++++++ src/CEF/OsrDragdropEvents.h | 30 + src/CEF/OsrDragdropWin.cpp | 652 ++++++++++++++ src/CEF/OsrDragdropWin.h | 183 ++++ src/CEF/OsrImeHandlerWin.cpp | 385 ++++++++ src/CEF/OsrImeHandlerWin.h | 108 +++ src/CEF/OsrRenderHandlerWin.cpp | 79 ++ src/CEF/OsrRenderHandlerWin.h | 74 ++ src/CEF/OsrRenderHandlerWinD3D11.cpp | 213 +++++ src/CEF/OsrRenderHandlerWinD3D11.h | 83 ++ src/CEF/OsrRenderHandlerWinGL.cpp | 196 +++++ src/CEF/OsrRenderHandlerWinGL.h | 49 ++ src/CEF/OsrRenderHandlerWinNative.cpp | 182 ++++ src/CEF/OsrRenderHandlerWinNative.h | 55 ++ src/CEF/OsrRenderer.cpp | 394 +++++++++ src/CEF/OsrRenderer.h | 67 ++ src/CEF/OsrRendererSettings.h | 13 +- src/CEF/OsrWindowWin.cpp | 379 ++++---- src/CEF/OsrWindowWin.h | 15 +- src/CEF/RootWindowCreate.cpp | 4 +- src/CEF/RootWindowWin.cpp | 246 ++++-- src/CEF/RootWindowWin.h | 22 +- src/CEF/SchemeTest.cpp | 10 +- src/CEF/SchemeTest.h | 5 +- src/CEF/TestRunner.cpp | 23 +- src/CEF/TestRunner.h | 14 +- src/CMakeLists.txt | 4 + 45 files changed, 5743 insertions(+), 355 deletions(-) create mode 100644 src/CEF/MainMessageLoopMultithreadedWin.cpp create mode 100644 src/CEF/MainMessageLoopMultithreadedWin.h create mode 100644 src/CEF/OsrAccessibilityNode.cpp create mode 100644 src/CEF/OsrAccessibilityNode.h create mode 100644 src/CEF/OsrAccessibilityNodeWin.cpp create mode 100644 src/CEF/OsrD3D11Win.cpp create mode 100644 src/CEF/OsrD3D11Win.h create mode 100644 src/CEF/OsrDragdropEvents.h create mode 100644 src/CEF/OsrDragdropWin.cpp create mode 100644 src/CEF/OsrDragdropWin.h create mode 100644 src/CEF/OsrImeHandlerWin.cpp create mode 100644 src/CEF/OsrImeHandlerWin.h create mode 100644 src/CEF/OsrRenderHandlerWin.cpp create mode 100644 src/CEF/OsrRenderHandlerWin.h create mode 100644 src/CEF/OsrRenderHandlerWinD3D11.cpp create mode 100644 src/CEF/OsrRenderHandlerWinD3D11.h create mode 100644 src/CEF/OsrRenderHandlerWinGL.cpp create mode 100644 src/CEF/OsrRenderHandlerWinGL.h create mode 100644 src/CEF/OsrRenderHandlerWinNative.cpp create mode 100644 src/CEF/OsrRenderHandlerWinNative.h create mode 100644 src/CEF/OsrRenderer.cpp create mode 100644 src/CEF/OsrRenderer.h diff --git a/src/CEF/BrowserWindow.h b/src/CEF/BrowserWindow.h index e8fbca5..876d79c 100644 --- a/src/CEF/BrowserWindow.h +++ b/src/CEF/BrowserWindow.h @@ -100,6 +100,10 @@ class BrowserWindow : public ClientHandler::Delegate { // Returns the window handle. virtual ClientWindowHandle GetWindowHandle() const = 0; +#if defined(OS_WIN) + virtual LRESULT OnMessage(CefWindowHandle temp_handle, UINT message, WPARAM wParam, LPARAM lParam) = 0; +#endif + // Returns the browser owned by the window. CefRefPtr GetBrowser() const; diff --git a/src/CEF/BrowserWindowOsrWin.cpp b/src/CEF/BrowserWindowOsrWin.cpp index 8c3ae83..c5a2c86 100644 --- a/src/CEF/BrowserWindowOsrWin.cpp +++ b/src/CEF/BrowserWindowOsrWin.cpp @@ -40,7 +40,7 @@ void BrowserWindowOsrWin::GetPopupConfig(CefWindowHandle temp_handle, windowInfo.SetAsWindowless(temp_handle); windowInfo.shared_texture_enabled = - osr_window_->settings().shared_texture_enabled; + osr_window_->settings().render_type == OsrRendererSettings::OsrRendererType::D3D11; windowInfo.external_begin_frame_enabled = osr_window_->settings().external_begin_frame_enabled; @@ -109,6 +109,15 @@ ClientWindowHandle BrowserWindowOsrWin::GetWindowHandle() const { return osr_hwnd_; } +#if defined(OS_WIN) +LRESULT BrowserWindowOsrWin::OnMessage(HWND temp_handle, UINT message, WPARAM wParam, LPARAM lParam) { + if (osr_window_) { + return osr_window_->OnMessage(temp_handle, message, wParam, lParam); + } + return 0; +} +#endif + void BrowserWindowOsrWin::OnBrowserClosed(CefRefPtr browser) { REQUIRE_MAIN_THREAD(); diff --git a/src/CEF/BrowserWindowOsrWin.h b/src/CEF/BrowserWindowOsrWin.h index d7cb52a..cf65553 100644 --- a/src/CEF/BrowserWindowOsrWin.h +++ b/src/CEF/BrowserWindowOsrWin.h @@ -5,7 +5,7 @@ #pragma once #include "CEF/BrowserWindow.h" -#include "CEF/osr_window_win.h" +#include "CEF/OsrWindowWin.h" // Represents a native child window hosting a single off-screen browser // instance. The methods of this class must be called on the main thread unless @@ -42,6 +42,9 @@ class BrowserWindowOsrWin : public BrowserWindow, void SetDeviceScaleFactor(float device_scale_factor) override; float GetDeviceScaleFactor() const override; ClientWindowHandle GetWindowHandle() const override; +#if defined(OS_WIN) + virtual LRESULT OnMessage(CefWindowHandle temp_handle, UINT message, WPARAM wParam, LPARAM lParam) override; +#endif private: // ClienHandler::Delegate methods. diff --git a/src/CEF/BrowserWindowStdWin.cpp b/src/CEF/BrowserWindowStdWin.cpp index b8fa24c..ce07885 100644 --- a/src/CEF/BrowserWindowStdWin.cpp +++ b/src/CEF/BrowserWindowStdWin.cpp @@ -2,12 +2,10 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. -#include "tests/cefclient/browser/browser_window_std_win.h" +#include "CEF/BrowserWindowStdWin.h" -#include "tests/cefclient/browser/client_handler_std.h" -#include "tests/shared/browser/main_message_loop.h" - -namespace client { +#include "CEF/ClientHandlerStd.h" +#include "CEF/MainMessageLoop.h" BrowserWindowStdWin::BrowserWindowStdWin(Delegate* delegate, bool with_controls, @@ -116,5 +114,3 @@ ClientWindowHandle BrowserWindowStdWin::GetWindowHandle() const { return browser_->GetHost()->GetWindowHandle(); return nullptr; } - -} // namespace client diff --git a/src/CEF/BrowserWindowStdWin.h b/src/CEF/BrowserWindowStdWin.h index 5a7ee2a..3e3eb00 100644 --- a/src/CEF/BrowserWindowStdWin.h +++ b/src/CEF/BrowserWindowStdWin.h @@ -2,13 +2,8 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. -#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_WIN_H_ -#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_WIN_H_ -#pragma once -#include "tests/cefclient/browser/browser_window.h" - -namespace client { +#include "CEF/BrowserWindow.h" // Represents a native child window hosting a single windowed browser instance. // The methods of this class must be called on the main thread unless otherwise @@ -41,11 +36,12 @@ class BrowserWindowStdWin : public BrowserWindow { void SetBounds(int x, int y, size_t width, size_t height) override; void SetFocus(bool focus) override; ClientWindowHandle GetWindowHandle() const override; +#if defined(OS_WIN) + virtual LRESULT OnMessage(CefWindowHandle temp_handle, UINT message, WPARAM wParam, LPARAM lParam) override { + return 0; + } +#endif private: DISALLOW_COPY_AND_ASSIGN(BrowserWindowStdWin); }; - -} // namespace client - -#endif // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_WIN_H_ diff --git a/src/CEF/ClientHandler.cpp b/src/CEF/ClientHandler.cpp index f5288bd..5943615 100644 --- a/src/CEF/ClientHandler.cpp +++ b/src/CEF/ClientHandler.cpp @@ -975,7 +975,7 @@ CefRefPtr ClientHandler::GetResourceResponseFilter( CefRefPtr response) { CEF_REQUIRE_IO_THREAD(); - return GetResourceResponseFilter(browser, frame, request, + return test_runner::GetResourceResponseFilter(browser, frame, request, response); } diff --git a/src/CEF/ClientHandlerOSR.h b/src/CEF/ClientHandlerOSR.h index 59c6fdd..e9f3c06 100644 --- a/src/CEF/ClientHandlerOSR.h +++ b/src/CEF/ClientHandlerOSR.h @@ -66,6 +66,7 @@ class ClientHandlerOsr : public ClientHandler, virtual void UpdateAccessibilityTree(CefRefPtr value) = 0; virtual void UpdateAccessibilityLocation(CefRefPtr value) = 0; + virtual void OnSize() = 0; protected: virtual ~OsrDelegate() {} diff --git a/src/CEF/HumanAppContextImpl.cpp b/src/CEF/HumanAppContextImpl.cpp index 56f1bcd..2fed9f6 100644 --- a/src/CEF/HumanAppContextImpl.cpp +++ b/src/CEF/HumanAppContextImpl.cpp @@ -133,7 +133,7 @@ HumanAppContextImpl::HumanAppContextImpl(CefRefPtr command_line, if (background_color_ == 0 && !use_views_) { // Set an explicit background color. - background_color_ = CefColorSetARGB(255, 255, 255, 255); + background_color_ = CefColorSetARGB(0, 255, 255, 255); } // |browser_background_color_| should remain 0 to enable transparent painting. @@ -220,7 +220,8 @@ void HumanAppContextImpl::PopulateOsrSettings(OsrRendererSettings* settings) { command_line_->HasSwitch(kShowUpdateRect); #if defined(OS_WIN) - settings->shared_texture_enabled = shared_texture_enabled_; + settings->render_type = shared_texture_enabled_ ? + OsrRendererSettings::OsrRendererType::D3D11 : OsrRendererSettings::OsrRendererType::Navtive; #endif settings->external_begin_frame_enabled = external_begin_frame_enabled_; settings->begin_frame_rate = windowless_frame_rate_; diff --git a/src/CEF/HumanAppContextImpl.h b/src/CEF/HumanAppContextImpl.h index 355c710..27ec79b 100644 --- a/src/CEF/HumanAppContextImpl.h +++ b/src/CEF/HumanAppContextImpl.h @@ -10,6 +10,7 @@ #include "include/cef_app.h" #include "include/cef_command_line.h" #include "CEF/HumanAppContext.h" +#include "CEF/RootWindoManager.h" diff --git a/src/CEF/Main.cpp b/src/CEF/Main.cpp index cb446e2..d8c5095 100644 --- a/src/CEF/Main.cpp +++ b/src/CEF/Main.cpp @@ -1,6 +1,7 @@ -#if 1 + #include +#include "HumanAppContextImpl.h" #include "Core/Core.h" #include "include/cef_command_line.h" @@ -8,6 +9,11 @@ #include "CEF/HumanAppBrowser.h" #include "CEF/HumanAppRenderer.h" #include "CEF/HumanAppOther.h" +#include "CEF/MainMessageLoopMultithreadedWin.h" +#include "CEF/MainMessageLoopExternalPump.h" +#include "CEF/TestRunner.h" +#include "CEF/RootWindow.h" +#include "CEF/HumanAppSwitches.h" static int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, @@ -31,32 +37,23 @@ static int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevI // Provide CEF with command-line arguments. CefMainArgs main_args(hInstance); - // CEF applications have multiple sub-processes (render, GPU, etc) that share - // the same executable. This function checks the command-line and, if this is - // a sub-process, executes the appropriate logic. - int exit_code = CefExecuteProcess(main_args, nullptr, sandbox_info); - if (exit_code >= 0) { - // The sub-process has completed so return here. - return exit_code; - } - // Parse command-line arguments for use in this method. CefRefPtr command_line = CefCommandLine::CreateCommandLine(); command_line->InitFromString(::GetCommandLineW()); - // Specify CEF global settings here. - CefSettings settings; - - if (command_line->HasSwitch("enable-chrome-runtime")) { - // Enable experimental Chrome runtime. See issue #2969 for details. - settings.chrome_runtime = true; - } - -#if !defined(CEF_USE_SANDBOX) - settings.no_sandbox = true; -#endif - - // SimpleApp implements application-level callbacks for the browser process. +// // Specify CEF global settings here. +// CefSettings settings; +// +// if (command_line->HasSwitch("enable-chrome-runtime")) { +// // Enable experimental Chrome runtime. See issue #2969 for details. +// settings.chrome_runtime = true; +// } +// +// #if !defined(CEF_USE_SANDBOX) +// settings.no_sandbox = true; +// #endif +// +// // SimpleApp implements application-level callbacks for the browser process. // It will create the first browser instance in OnContextInitialized() after // CEF has initialized. CefRefPtr app; @@ -68,16 +65,64 @@ static int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevI else if (process_type == HumanApp::OtherProcess) app = new HumanAppOther(); - // Initialize CEF. - CefInitialize(main_args, settings, app.get(), sandbox_info); + int exit_code = CefExecuteProcess(main_args, app, sandbox_info); + if (exit_code >= 0) + return exit_code; - // Run the CEF message loop. This will block until CefQuitMessageLoop() is - // called. - CefRunMessageLoop(); + // Create the main context object. + auto context = std::make_unique(command_line, true); + + CefSettings settings; + +#if !defined(CEF_USE_SANDBOX) + settings.no_sandbox = true; +#endif + + // Populate the settings based on command line arguments. + context->PopulateSettings(&settings); + + settings.windowless_rendering_enabled = true; + settings.background_color = CefColorSetARGB(0, 0, 0, 0); + + // Create the main message loop object. + std::unique_ptr message_loop; + if (settings.multi_threaded_message_loop) + message_loop.reset(new MainMessageLoopMultithreadedWin); + else if (settings.external_message_pump) + message_loop = MainMessageLoopExternalPump::Create(); + else + message_loop.reset(new MainMessageLoopStd); + + // Initialize CEF. + context->Initialize(main_args, settings, app, sandbox_info); + + // Register scheme handlers. + test_runner::RegisterSchemeHandlers(); + + auto window_config = std::make_unique(); + window_config->always_on_top = + command_line->HasSwitch(kAlwaysOnTop); + window_config->with_controls = + command_line->HasSwitch(kHideControls); + window_config->with_osr = + settings.windowless_rendering_enabled ? true : false; + + window_config->url = "file:///C:/Users/jiege/Downloads/lrc%20demo/lrc%20demo.html"; + + // Create the first window. + context->GetRootWindowManager()->CreateRootWindow(std::move(window_config)); + + // Run the message loop. This will block until Quit() is called by the + // RootWindowManager after all windows have been destroyed. + int result = message_loop->Run(); // Shut down CEF. - CefShutdown(); + context->Shutdown(); - return 0; + // Release objects in reverse order of creation. + message_loop.reset(); + context.reset(); + + return result; } -#endif + diff --git a/src/CEF/MainMessageLoopMultithreadedWin.cpp b/src/CEF/MainMessageLoopMultithreadedWin.cpp new file mode 100644 index 0000000..8ad1448 --- /dev/null +++ b/src/CEF/MainMessageLoopMultithreadedWin.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2015 The Chromium Embedded Framework 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 "CEF/MainMessageLoopMultithreadedWin.h" + +#include "include/base/cef_callback.h" +#include "include/base/cef_logging.h" +#include "include/cef_app.h" +#include "CEF/resource.h" +#include "CEF/UtilWin.h" + +const wchar_t kWndClass[] = L"Client_MessageWindow"; +const wchar_t kTaskMessageName[] = L"Client_CustomTask"; + +MainMessageLoopMultithreadedWin::MainMessageLoopMultithreadedWin() + : thread_id_(base::PlatformThread::CurrentId()), + task_message_id_(RegisterWindowMessage(kTaskMessageName)), + dialog_hwnd_(nullptr), + message_hwnd_(nullptr) {} + +MainMessageLoopMultithreadedWin::~MainMessageLoopMultithreadedWin() { + DCHECK(RunsTasksOnCurrentThread()); + DCHECK(!message_hwnd_); + DCHECK(queued_tasks_.empty()); +} + +int MainMessageLoopMultithreadedWin::Run() { + DCHECK(RunsTasksOnCurrentThread()); + + HINSTANCE hInstance = ::GetModuleHandle(nullptr); + + { + base::AutoLock lock_scope(lock_); + + // Create the hidden window for message processing. + message_hwnd_ = CreateMessageWindow(hInstance); + CHECK(message_hwnd_); + + // Store a pointer to |this| in the window's user data. + SetUserDataPtr(message_hwnd_, this); + + // Execute any tasks that are currently queued. + while (!queued_tasks_.empty()) { + PostTaskInternal(queued_tasks_.front()); + queued_tasks_.pop(); + } + } + + HACCEL hAccelTable = + LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT)); + + MSG msg; + + // Run the application message loop. + while (GetMessage(&msg, nullptr, 0, 0)) { + // Allow processing of dialog messages. + if (dialog_hwnd_ && IsDialogMessage(dialog_hwnd_, &msg)) + continue; + + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + { + base::AutoLock lock_scope(lock_); + + // Destroy the message window. + DestroyWindow(message_hwnd_); + message_hwnd_ = nullptr; + } + + return static_cast(msg.wParam); +} + +void MainMessageLoopMultithreadedWin::Quit() { + // Execute PostQuitMessage(0) on the main thread. + PostClosure(base::BindOnce(::PostQuitMessage, 0)); +} + +void MainMessageLoopMultithreadedWin::PostTask(CefRefPtr task) { + base::AutoLock lock_scope(lock_); + PostTaskInternal(task); +} + +bool MainMessageLoopMultithreadedWin::RunsTasksOnCurrentThread() const { + return (thread_id_ == base::PlatformThread::CurrentId()); +} + +void MainMessageLoopMultithreadedWin::SetCurrentModelessDialog( + HWND hWndDialog) { + DCHECK(RunsTasksOnCurrentThread()); + +#if DCHECK_IS_ON() + if (hWndDialog) { + // A new dialog reference should not be set while one is currently set. + DCHECK(!dialog_hwnd_); + } +#endif + dialog_hwnd_ = hWndDialog; +} + +// static +HWND MainMessageLoopMultithreadedWin::CreateMessageWindow(HINSTANCE hInstance) { + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = MessageWndProc; + wc.hInstance = hInstance; + wc.lpszClassName = kWndClass; + RegisterClassEx(&wc); + + return CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, + 0); +} + +// static +LRESULT CALLBACK +MainMessageLoopMultithreadedWin::MessageWndProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) { + MainMessageLoopMultithreadedWin* self = + GetUserDataPtr(hWnd); + + if (self && message == self->task_message_id_) { + // Execute the task. + CefTask* task = reinterpret_cast(wParam); + task->Execute(); + + // Release the reference added in PostTaskInternal. This will likely result + // in |task| being deleted. + task->Release(); + } else { + switch (message) { + case WM_NCDESTROY: + // Clear the reference to |self|. + SetUserDataPtr(hWnd, nullptr); + break; + } + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +void MainMessageLoopMultithreadedWin::PostTaskInternal( + CefRefPtr task) { + lock_.AssertAcquired(); + + if (!message_hwnd_) { + // Queue the task until the message loop starts running. + queued_tasks_.push(task); + return; + } + + // Add a reference that will be released in MessageWndProc. + task->AddRef(); + + // Post the task for execution by the message window. + PostMessage(message_hwnd_, task_message_id_, + reinterpret_cast(task.get()), 0); +} diff --git a/src/CEF/MainMessageLoopMultithreadedWin.h b/src/CEF/MainMessageLoopMultithreadedWin.h new file mode 100644 index 0000000..de2a8a7 --- /dev/null +++ b/src/CEF/MainMessageLoopMultithreadedWin.h @@ -0,0 +1,55 @@ +// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#pragma once + +#include +#include + +#include "include/base/cef_lock.h" +#include "include/base/cef_platform_thread.h" +#include "CEF/MainMessageLoop.h" + +// Represents the main message loop in the browser process when using multi- +// threaded message loop mode on Windows. In this mode there is no Chromium +// message loop running on the main application thread. Instead, this +// implementation utilizes a hidden message window for running tasks. +class MainMessageLoopMultithreadedWin : public MainMessageLoop { + public: + MainMessageLoopMultithreadedWin(); + ~MainMessageLoopMultithreadedWin(); + + // MainMessageLoop methods. + int Run() override; + void Quit() override; + void PostTask(CefRefPtr task) override; + bool RunsTasksOnCurrentThread() const override; + void SetCurrentModelessDialog(HWND hWndDialog) override; + + private: + // Create the message window. + static HWND CreateMessageWindow(HINSTANCE hInstance); + + // Window procedure for the message window. + static LRESULT CALLBACK MessageWndProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam); + + void PostTaskInternal(CefRefPtr task); + + base::PlatformThreadId thread_id_; + UINT task_message_id_; + + // Only accessed on the main thread. + HWND dialog_hwnd_; + + base::Lock lock_; + + // Must be protected by |lock_|. + HWND message_hwnd_; + std::queue> queued_tasks_; + + DISALLOW_COPY_AND_ASSIGN(MainMessageLoopMultithreadedWin); +}; diff --git a/src/CEF/OsrAccessibilityHelper.cpp b/src/CEF/OsrAccessibilityHelper.cpp index 01fafc0..c21c1fe 100644 --- a/src/CEF/OsrAccessibilityHelper.cpp +++ b/src/CEF/OsrAccessibilityHelper.cpp @@ -2,10 +2,8 @@ // 2013 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 "tests/cefclient/browser/osr_accessibility_helper.h" -#include "tests/cefclient/browser/osr_accessibility_node.h" - -namespace client { +#include "CEF/OsrAccessibilityHelper.h" +#include "CEF/OsrAccessibilityNode.h" OsrAXTree::OsrAXTree() : root_node_id_(-1) {} @@ -266,5 +264,3 @@ OsrAXNode* OsrAccessibilityHelper::GetNode(const CefString& treeId, return nullptr; } - -} // namespace client diff --git a/src/CEF/OsrAccessibilityHelper.h b/src/CEF/OsrAccessibilityHelper.h index 83f271e..12b5e1a 100644 --- a/src/CEF/OsrAccessibilityHelper.h +++ b/src/CEF/OsrAccessibilityHelper.h @@ -2,14 +2,10 @@ // 2013 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. -#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_ -#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_ - #include #include "include/cef_browser.h" -namespace client { class OsrAXNode; class OsrAccessibilityHelper; @@ -75,7 +71,3 @@ class OsrAccessibilityHelper { CefRefPtr browser_; std::map accessibility_node_map_; }; - -} // namespace client - -#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_ diff --git a/src/CEF/OsrAccessibilityNode.cpp b/src/CEF/OsrAccessibilityNode.cpp new file mode 100644 index 0000000..8786a85 --- /dev/null +++ b/src/CEF/OsrAccessibilityNode.cpp @@ -0,0 +1,169 @@ +// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright +// 2013 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. + +// Base class implementation for CEF Acccessibility node. This is subclassed and +// used by both IAccessible/NSAccessibility protocol implementation. + +#include "CEF/OsrAccessibilityNode.h" + +#include "CEF/OsrAccessibilityHelper.h" + +OsrAXNode::OsrAXNode(const CefString& treeId, + int nodeId, + CefRefPtr value, + OsrAccessibilityHelper* helper) + : tree_id_(treeId), + node_id_(nodeId), + platform_accessibility_(nullptr), + parent_(nullptr), + offset_container_id_(-1), + accessibility_helper_(helper) { + UpdateValue(value); +} + +void OsrAXNode::UpdateLocation(CefRefPtr value) { + // Update Bounds + if (value->HasKey("bounds")) { + CefRefPtr loc = value->GetDictionary("bounds"); + if (loc) { + location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"), + loc->GetDouble("width"), loc->GetDouble("height")); + } + } + // Update offsets + if (value->HasKey("offset_container_id")) { + offset_container_id_ = OsrAccessibilityHelper::CastToInt( + value->GetValue("offset_container_id")); + } +} + +void OsrAXNode::UpdateValue(CefRefPtr value) { + if (value->HasKey("role")) + role_ = value->GetString("role"); + + if (value->HasKey("child_ids")) { + CefRefPtr childs = value->GetList("child_ids"); + // Reset child Ids + child_ids_.clear(); + for (size_t idx = 0; idx < childs->GetSize(); idx++) + child_ids_.push_back( + OsrAccessibilityHelper::CastToInt(childs->GetValue(idx))); + } + // Update Location + if (value->HasKey("location")) { + CefRefPtr loc = value->GetDictionary("location"); + if (loc) { + location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"), + loc->GetDouble("width"), loc->GetDouble("height")); + } + } + // Update offsets + if (value->HasKey("offset_container_id")) { + offset_container_id_ = OsrAccessibilityHelper::CastToInt( + value->GetValue("offset_container_id")); + } + // Update attributes + if (value->HasKey("attributes")) { + child_tree_id_ = ""; + + attributes_ = value->GetDictionary("attributes"); + + if (attributes_) { + scroll_.x = attributes_->HasKey("scrollX") + ? OsrAccessibilityHelper::CastToInt( + attributes_->GetValue("scrollX")) + : 0; + scroll_.y = attributes_->HasKey("scrollY") + ? OsrAccessibilityHelper::CastToInt( + attributes_->GetValue("scrollY")) + : 0; + } + + if (attributes_ && attributes_->HasKey("childTreeId")) { + child_tree_id_ = attributes_->GetString("childTreeId"); + } + if (attributes_ && attributes_->HasKey("name")) + name_ = attributes_->GetString("name"); + if (attributes_ && attributes_->HasKey("value")) + value_ = attributes_->GetString("value"); + if (attributes_ && attributes_->HasKey("description")) + description_ = attributes_->GetString("description"); + } +} + +CefWindowHandle OsrAXNode::GetWindowHandle() const { + if (accessibility_helper_) + return accessibility_helper_->GetWindowHandle(); + return nullptr; +} + +CefRefPtr OsrAXNode::GetBrowser() const { + if (accessibility_helper_) + return accessibility_helper_->GetBrowser(); + return nullptr; +} + +void OsrAXNode::SetParent(OsrAXNode* parent) { + parent_ = parent; +} + +CefRect OsrAXNode::AxLocation() const { + CefRect loc = location_; + loc.x -= scroll_.x; + loc.y -= scroll_.y; + OsrAXNode* offsetNode = + accessibility_helper_->GetNode(OsrAXTreeId(), offset_container_id_); + if (!offsetNode) { + OsrAXNode* p = parent_; + while (p) { + if (p->OsrAXTreeId() != OsrAXTreeId()) { + offsetNode = p; + break; + } + p = p->parent_; + } + } + // Add offset from parent Location + if (offsetNode) { + CefRect offset = offsetNode->AxLocation(); + loc.x += offset.x; + loc.y += offset.y; + } + return loc; +} + +int OsrAXNode::GetChildCount() const { + int count = static_cast(child_ids_.size()); + if (!child_tree_id_.empty()) { + OsrAXNode* childTreeRootNode = + accessibility_helper_->GetTreeRootNode(child_tree_id_); + if (childTreeRootNode) { + count++; + } + } + return count; +} + +OsrAXNode* OsrAXNode::ChildAtIndex(int index) const { + int count = static_cast(child_ids_.size()); + if (index < count) + return accessibility_helper_->GetNode(OsrAXTreeId(), child_ids_[index]); + if ((index == count) && (!child_tree_id_.empty())) { + OsrAXNode* childTreeRootNode = + accessibility_helper_->GetTreeRootNode(child_tree_id_); + if (childTreeRootNode) { + return childTreeRootNode; + } + } + + return nullptr; +} + +// Create and return the platform specific OsrAXNode Object +OsrAXNode* OsrAXNode::CreateNode(const CefString& treeId, + int nodeId, + CefRefPtr value, + OsrAccessibilityHelper* helper) { + return new OsrAXNode(treeId, nodeId, value, helper); +} diff --git a/src/CEF/OsrAccessibilityNode.h b/src/CEF/OsrAccessibilityNode.h new file mode 100644 index 0000000..1b789ec --- /dev/null +++ b/src/CEF/OsrAccessibilityNode.h @@ -0,0 +1,116 @@ +// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright +// 2013 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. + + +#pragma once + +#include + +#include "include/cef_browser.h" + +#if defined(OS_MAC) +typedef void CefNativeAccessible; +#if __OBJC__ +#if __has_feature(objc_arc) +#define CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(accessible) \ + (__bridge NSObject*)accessible +#define CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(object) \ + (__bridge CefNativeAccessible*)object +#else // __has_feature(objc_arc) +#define CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(accessible) (NSObject*)accessible +#define CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(object) \ + (__bridge CefNativeAccessible*)object +#endif // __has_feature(objc_arc) +#endif // __OBJC__ +#elif defined(OS_WIN) +struct IAccessible; +typedef IAccessible CefNativeAccessible; +#else +#error "Unsupported platform" +#endif + +class OsrAccessibilityHelper; + +// OsrAXNode is the base class for implementation for the NSAccessibility +// protocol for interacting with VoiceOver and other accessibility clients. +class OsrAXNode { + public: + // Create and return the platform specific OsrAXNode Object. + static OsrAXNode* CreateNode(const CefString& treeId, + int nodeId, + CefRefPtr value, + OsrAccessibilityHelper* helper); + + // Update Value. + void UpdateValue(CefRefPtr value); + + // UpdateLocation + void UpdateLocation(CefRefPtr value); + + // Fire a platform-specific notification that an event has occurred on + // this object. + void NotifyAccessibilityEvent(std::string event_type) const; + + // Call Destroy rather than deleting this, because the subclass may + // use reference counting. + void Destroy(); + + // Return NSAccessibility Object for Mac/ IAccessible for Windows + CefNativeAccessible* GetNativeAccessibleObject(OsrAXNode* parent); + + CefNativeAccessible* GetParentAccessibleObject() const { + return parent_ ? parent_->platform_accessibility_ : nullptr; + } + + OsrAccessibilityHelper* GetAccessibilityHelper() const { + return accessibility_helper_; + } + + int GetChildCount() const; + + // Return the Child at the specified index + OsrAXNode* ChildAtIndex(int index) const; + + const CefString& AxRole() const { return role_; } + + const CefString& OsrAXTreeId() const { return tree_id_; } + + int OsrAXNodeId() const { return node_id_; } + + const CefString& AxValue() const { return value_; } + + const CefString& AxName() const { return name_; } + + const CefString& AxDescription() const { return description_; } + + CefRect AxLocation() const; + + CefWindowHandle GetWindowHandle() const; + + CefRefPtr GetBrowser() const; + + void SetParent(OsrAXNode* parent); + + protected: + OsrAXNode(const CefString& treeId, + int nodeId, + CefRefPtr value, + OsrAccessibilityHelper* helper); + + CefString tree_id_; + int node_id_; + CefString child_tree_id_; + CefString role_; + CefString value_; + CefString name_; + CefString description_; + CefRect location_; + CefPoint scroll_; + std::vector child_ids_; + CefNativeAccessible* platform_accessibility_; + OsrAXNode* parent_; + int offset_container_id_; + OsrAccessibilityHelper* accessibility_helper_; + CefRefPtr attributes_; +}; diff --git a/src/CEF/OsrAccessibilityNodeWin.cpp b/src/CEF/OsrAccessibilityNodeWin.cpp new file mode 100644 index 0000000..b1023cc --- /dev/null +++ b/src/CEF/OsrAccessibilityNodeWin.cpp @@ -0,0 +1,684 @@ +// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright +// 2013 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. + +// This class implements our accessible proxy object that handles moving +// data back and forth between MSAA clients and CefClient renderers. +// Sample implementation based on ui\accessibility\ax_platform_node_win.h + +#include "CEF/OsrAccessibilityNode.h" + +#if defined(CEF_USE_ATL) + +#include +#include +#include + +#include "CEF/OsrAccessibilityHelper.h" + +// Return CO_E_OBJNOTCONNECTED for accessible objects thar still exists but the +// window and/or object it references has been destroyed. +#define DATACHECK(node) (node) ? S_OK : CO_E_OBJNOTCONNECTED +#define VALID_CHILDID(varChild) ((varChild.vt == VT_I4)) + +// Helper function to convert a rectangle from client coordinates to screen +// coordinates. +void ClientToScreen(HWND hwnd, LPRECT lpRect) { + if (lpRect) { + POINT ptTL = {lpRect->left, lpRect->top}; + POINT ptBR = {lpRect->right, lpRect->bottom}; + // Win32 API only provides the call for a point. + ClientToScreen(hwnd, &ptTL); + ClientToScreen(hwnd, &ptBR); + SetRect(lpRect, ptTL.x, ptTL.y, ptBR.x, ptBR.y); + } +} + +// Helper function to convert to MSAARole +int AxRoleToMSAARole(const std::string& role_string) { + if (role_string == "alert") + return ROLE_SYSTEM_ALERT; + if (role_string == "application") + return ROLE_SYSTEM_APPLICATION; + if (role_string == "buttonDropDown") + return ROLE_SYSTEM_BUTTONDROPDOWN; + if (role_string == "popUpButton") + return ROLE_SYSTEM_BUTTONMENU; + if (role_string == "checkBox") + return ROLE_SYSTEM_CHECKBUTTON; + if (role_string == "comboBox") + return ROLE_SYSTEM_COMBOBOX; + if (role_string == "dialog") + return ROLE_SYSTEM_DIALOG; + if (role_string == "genericContainer") + return ROLE_SYSTEM_GROUPING; + if (role_string == "group") + return ROLE_SYSTEM_GROUPING; + if (role_string == "image") + return ROLE_SYSTEM_GRAPHIC; + if (role_string == "link") + return ROLE_SYSTEM_LINK; + if (role_string == "locationBar") + return ROLE_SYSTEM_GROUPING; + if (role_string == "menuBar") + return ROLE_SYSTEM_MENUBAR; + if (role_string == "menuItem") + return ROLE_SYSTEM_MENUITEM; + if (role_string == "menuListPopup") + return ROLE_SYSTEM_MENUPOPUP; + if (role_string == "tree") + return ROLE_SYSTEM_OUTLINE; + if (role_string == "treeItem") + return ROLE_SYSTEM_OUTLINEITEM; + if (role_string == "tab") + return ROLE_SYSTEM_PAGETAB; + if (role_string == "tabList") + return ROLE_SYSTEM_PAGETABLIST; + if (role_string == "pane") + return ROLE_SYSTEM_PANE; + if (role_string == "progressIndicator") + return ROLE_SYSTEM_PROGRESSBAR; + if (role_string == "button") + return ROLE_SYSTEM_PUSHBUTTON; + if (role_string == "radioButton") + return ROLE_SYSTEM_RADIOBUTTON; + if (role_string == "scrollBar") + return ROLE_SYSTEM_SCROLLBAR; + if (role_string == "splitter") + return ROLE_SYSTEM_SEPARATOR; + if (role_string == "slider") + return ROLE_SYSTEM_SLIDER; + if (role_string == "staticText") + return ROLE_SYSTEM_STATICTEXT; + if (role_string == "textField") + return ROLE_SYSTEM_TEXT; + if (role_string == "titleBar") + return ROLE_SYSTEM_TITLEBAR; + if (role_string == "toolbar") + return ROLE_SYSTEM_TOOLBAR; + if (role_string == "webView") + return ROLE_SYSTEM_GROUPING; + if (role_string == "window") + return ROLE_SYSTEM_WINDOW; + if (role_string == "client") + return ROLE_SYSTEM_CLIENT; + // This is the default role for MSAA. + return ROLE_SYSTEM_CLIENT; +} + +static inline int MiddleX(const CefRect& rect) { + return rect.x + rect.width / 2; +} + +static inline int MiddleY(const CefRect& rect) { + return rect.y + rect.height / 2; +} + +struct CefIAccessible : public IAccessible { + public: + // Implement IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // + // IAccessible methods. + // + // Retrieves the child element or child object at a given point on the screen. + STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child) override; + + // Performs the object's default action. + STDMETHODIMP accDoDefaultAction(VARIANT var_id) override; + + // Retrieves the specified object's current screen location. + STDMETHODIMP accLocation(LONG* x_left, + LONG* y_top, + LONG* width, + LONG* height, + VARIANT var_id) override; + + // Traverses to another UI element and retrieves the object. + STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end) override; + + // Retrieves an IDispatch interface pointer for the specified child. + STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override; + + // Retrieves the number of accessible children. + STDMETHODIMP get_accChildCount(LONG* child_count) override; + + // Retrieves a string that describes the object's default action. + STDMETHODIMP get_accDefaultAction(VARIANT var_id, + BSTR* default_action) override; + + // Retrieves the tooltip description. + STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override; + + // Retrieves the object that has the keyboard focus. + STDMETHODIMP get_accFocus(VARIANT* focus_child) override; + + // Retrieves the specified object's shortcut. + STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id, + BSTR* access_key) override; + + // Retrieves the name of the specified object. + STDMETHODIMP get_accName(VARIANT var_id, BSTR* name) override; + + // Retrieves the IDispatch interface of the object's parent. + STDMETHODIMP get_accParent(IDispatch** disp_parent) override; + + // Retrieves information describing the role of the specified object. + STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override; + + // Retrieves the current state of the specified object. + STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override; + + // Gets the help string for the specified object. + STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override; + + // Retrieve or set the string value associated with the specified object. + // Setting the value is not typically used by screen readers, but it's + // used frequently by automation software. + STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override; + STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override; + + // IAccessible methods not implemented. + STDMETHODIMP get_accSelection(VARIANT* selected) override; + STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override; + STDMETHODIMP get_accHelpTopic(BSTR* help_file, + VARIANT var_id, + LONG* topic_id) override; + STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; + + // Implement IDispatch + STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo) override; + STDMETHODIMP GetTypeInfo(unsigned int iTInfo, + LCID lcid, + ITypeInfo FAR* FAR* ppTInfo) override; + STDMETHODIMP GetIDsOfNames(REFIID riid, + OLECHAR FAR* FAR* rgszNames, + unsigned int cNames, + LCID lcid, + DISPID FAR* rgDispId) override; + STDMETHODIMP Invoke(DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pDispParams, + VARIANT FAR* pVarResult, + EXCEPINFO FAR* pExcepInfo, + unsigned int FAR* puArgErr) override; + + CefIAccessible(OsrAXNode* node) : ref_count_(0), node_(node) {} + + // Remove the node reference when OsrAXNode is destroyed, so that + // MSAA clients get CO_E_OBJNOTCONNECTED + void MarkDestroyed() { node_ = nullptr; } + + protected: + virtual ~CefIAccessible() {} + + // Ref Count + ULONG ref_count_; + // OsrAXNode* proxy object + OsrAXNode* node_; +}; + +// Implement IUnknown +// ********************* + +// Handles ref counting and querying for other supported interfaces. +// We only support, IUnknown, IDispatch and IAccessible. +STDMETHODIMP CefIAccessible::QueryInterface(REFIID riid, void** ppvObject) { + if (riid == IID_IAccessible) + *ppvObject = static_cast(this); + else if (riid == IID_IDispatch) + *ppvObject = static_cast(this); + else if (riid == IID_IUnknown) + *ppvObject = static_cast(this); + else + *ppvObject = nullptr; + + if (*ppvObject) + reinterpret_cast(*ppvObject)->AddRef(); + + return (*ppvObject) ? S_OK : E_NOINTERFACE; +} + +// Increments COM objects refcount required by IUnknown for reference counting +STDMETHODIMP_(ULONG) CefIAccessible::AddRef() { + return InterlockedIncrement((LONG volatile*)&ref_count_); +} + +STDMETHODIMP_(ULONG) CefIAccessible::Release() { + ULONG ulRefCnt = InterlockedDecrement((LONG volatile*)&ref_count_); + if (ulRefCnt == 0) { + // Remove reference from OsrAXNode + if (node_) + node_->Destroy(); + delete this; + } + + return ulRefCnt; +} + +// Implement IAccessible +// ********************* + +// Returns the parent IAccessible in the form of an IDispatch interface. +STDMETHODIMP CefIAccessible::get_accParent(IDispatch** ppdispParent) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (ppdispParent) { + CefNativeAccessible* parent = node_->GetParentAccessibleObject(); + if (!parent) { + // Find our parent window + HWND hWnd = ::GetParent(node_->GetWindowHandle()); + // if we have a window attempt to get its IAccessible pointer + if (hWnd) { + AccessibleObjectFromWindow(hWnd, (DWORD)OBJID_CLIENT, IID_IAccessible, + (void**)(&parent)); + } + } + + if (parent) + parent->AddRef(); + *ppdispParent = parent; + retCode = (*ppdispParent) ? S_OK : S_FALSE; + } + } else { + retCode = E_INVALIDARG; + } + return retCode; +} + +// Returns the number of children we have for this element. +STDMETHODIMP CefIAccessible::get_accChildCount(long* pcountChildren) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode) && pcountChildren) { + // Get Child node count for this from Accessibility tree + *pcountChildren = node_->GetChildCount(); + } else { + retCode = E_INVALIDARG; + } + return retCode; +} + +// Returns a child IAccessible object. +STDMETHODIMP CefIAccessible::get_accChild(VARIANT varChild, + IDispatch** ppdispChild) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + int numChilds = node_->GetChildCount(); + // Mark Leaf node if there are no child + if (numChilds <= 0) { + *ppdispChild = nullptr; + return S_FALSE; + } else { + if (ppdispChild && VALID_CHILDID(varChild)) { + if (varChild.lVal == CHILDID_SELF) { + *ppdispChild = this; + } else { + // Convert to 0 based index and get Child Node. + OsrAXNode* child = node_->ChildAtIndex(varChild.lVal - 1); + // Fallback to focused node + if (!child) + child = node_->GetAccessibilityHelper()->GetFocusedNode(); + + *ppdispChild = child->GetNativeAccessibleObject(node_); + } + if (*ppdispChild == nullptr) + retCode = S_FALSE; + else + (*ppdispChild)->AddRef(); + } + } + } + return retCode; +} + +// Check and returns the accessible name for element from accessibility tree +STDMETHODIMP CefIAccessible::get_accName(VARIANT varChild, BSTR* pszName) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pszName && VALID_CHILDID(varChild)) { + std::wstring name = node_->AxName(); + CComBSTR bstrResult(name.c_str()); + *pszName = bstrResult.Detach(); + } + } else { + retCode = E_INVALIDARG; + } + return retCode; +} + +// Check and returns the value for element from accessibility tree +STDMETHODIMP CefIAccessible::get_accValue(VARIANT varChild, BSTR* pszValue) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pszValue && VALID_CHILDID(varChild)) { + std::wstring name = node_->AxValue(); + CComBSTR bstrResult(name.c_str()); + *pszValue = bstrResult.Detach(); + } + } else { + retCode = E_INVALIDARG; + } + return retCode; +} + +// Check and returns the description for element from accessibility tree +STDMETHODIMP CefIAccessible::get_accDescription(VARIANT varChild, + BSTR* pszDescription) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pszDescription && VALID_CHILDID(varChild)) { + std::wstring name = node_->AxDescription(); + CComBSTR bstrResult(name.c_str()); + *pszDescription = bstrResult.Detach(); + } + } else { + retCode = E_INVALIDARG; + } + return retCode; +} + +// Check and returns the MSAA Role for element from accessibility tree +STDMETHODIMP CefIAccessible::get_accRole(VARIANT varChild, VARIANT* pvarRole) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + // Get the accessibilty role and Map to MSAA Role + if (pvarRole) { + pvarRole->vt = VT_I4; + pvarRole->lVal = AxRoleToMSAARole(node_->AxRole()); + } else { + retCode = E_INVALIDARG; + } + } + return retCode; +} + +// Check and returns Accessibility State for element from accessibility tree +STDMETHODIMP CefIAccessible::get_accState(VARIANT varChild, + VARIANT* pvarState) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pvarState) { + pvarState->vt = VT_I4; + pvarState->lVal = + (GetFocus() == node_->GetWindowHandle()) ? STATE_SYSTEM_FOCUSED : 0; + pvarState->lVal |= STATE_SYSTEM_PRESSED; + pvarState->lVal |= STATE_SYSTEM_FOCUSABLE; + + // For child + if (varChild.lVal == CHILDID_SELF) { + DWORD dwStyle = GetWindowLong(node_->GetWindowHandle(), GWL_STYLE); + pvarState->lVal |= + ((dwStyle & WS_VISIBLE) == 0) ? STATE_SYSTEM_INVISIBLE : 0; + pvarState->lVal |= + ((dwStyle & WS_DISABLED) > 0) ? STATE_SYSTEM_UNAVAILABLE : 0; + } + } else { + retCode = E_INVALIDARG; + } + } + return retCode; +} + +// Check and returns Accessibility Shortcut if any for element +STDMETHODIMP CefIAccessible::get_accKeyboardShortcut( + VARIANT varChild, + BSTR* pszKeyboardShortcut) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pszKeyboardShortcut && VALID_CHILDID(varChild)) + *pszKeyboardShortcut = ::SysAllocString(L"None"); + else + retCode = E_INVALIDARG; + } + return retCode; +} + +// Return focused element from the accessibility tree +STDMETHODIMP CefIAccessible::get_accFocus(VARIANT* pFocusChild) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + OsrAXNode* focusedNode = node_->GetAccessibilityHelper()->GetFocusedNode(); + CefNativeAccessible* nativeObj = nullptr; + if (focusedNode) + nativeObj = focusedNode->GetNativeAccessibleObject(nullptr); + + if (nativeObj) { + if (nativeObj == this) { + pFocusChild->vt = VT_I4; + pFocusChild->lVal = CHILDID_SELF; + } else { + pFocusChild->vt = VT_DISPATCH; + pFocusChild->pdispVal = nativeObj; + pFocusChild->pdispVal->AddRef(); + } + } else { + pFocusChild->vt = VT_EMPTY; + } + } + return retCode; +} + +// Return a selection list for multiple selection items. +STDMETHODIMP CefIAccessible::get_accSelection(VARIANT* pvarChildren) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pvarChildren) + pvarChildren->vt = VT_EMPTY; + else + retCode = E_INVALIDARG; + } + return retCode; +} + +// Return a string description of the default action of our element, eg. push +STDMETHODIMP CefIAccessible::get_accDefaultAction(VARIANT varChild, + BSTR* pszDefaultAction) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pszDefaultAction && VALID_CHILDID(varChild)) + *pszDefaultAction = ::SysAllocString(L"Push"); + else + retCode = E_INVALIDARG; + } + return retCode; +} + +// child item selectionor for an item to take focus. +STDMETHODIMP CefIAccessible::accSelect(long flagsSelect, VARIANT varChild) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (VALID_CHILDID(varChild)) { + HWND hwnd = node_->GetWindowHandle(); + // we only support SELFLAG_TAKEFOCUS. + if (((flagsSelect & SELFLAG_TAKEFOCUS) > 0) && (GetFocus() == hwnd)) { + RECT rcWnd; + GetClientRect(hwnd, &rcWnd); + InvalidateRect(hwnd, &rcWnd, FALSE); + } else { + retCode = S_FALSE; + } + } else { + retCode = E_INVALIDARG; + } + } + + return retCode; +} + +// Returns back the screen coordinates of our element or one of its childs +STDMETHODIMP CefIAccessible::accLocation(long* pxLeft, + long* pyTop, + long* pcxWidth, + long* pcyHeight, + VARIANT varChild) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pxLeft && pyTop && pcxWidth && pcyHeight && VALID_CHILDID(varChild)) { + CefRect loc = node_->AxLocation(); + RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height}; + HWND hwnd = node_->GetWindowHandle(); + ClientToScreen(hwnd, &rcItem); + + *pxLeft = rcItem.left; + *pyTop = rcItem.top; + *pcxWidth = rcItem.right - rcItem.left; + *pcyHeight = rcItem.bottom - rcItem.top; + } else { + retCode = E_INVALIDARG; + } + } + return retCode; +} + +// Allow clients to move the keyboard focus within the control +// Deprecated +STDMETHODIMP CefIAccessible::accNavigate(long navDir, + VARIANT varStart, + VARIANT* pvarEndUpAt) { + return E_NOTIMPL; +} + +// Check if the coordinates provided are within our element or child items. +STDMETHODIMP CefIAccessible::accHitTest(long xLeft, + long yTop, + VARIANT* pvarChild) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode)) { + if (pvarChild) { + pvarChild->vt = VT_EMPTY; + + CefRect loc = node_->AxLocation(); + RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height}; + POINT pt = {xLeft, yTop}; + + ClientToScreen(node_->GetWindowHandle(), &rcItem); + + if (PtInRect(&rcItem, pt)) { + pvarChild->vt = VT_I4; + pvarChild->lVal = 1; + } + } else { + retCode = E_INVALIDARG; + } + } + + return retCode; +} + +// Forces the default action of our element. In simplest cases, send a click. +STDMETHODIMP CefIAccessible::accDoDefaultAction(VARIANT varChild) { + HRESULT retCode = DATACHECK(node_); + if (SUCCEEDED(retCode) && VALID_CHILDID(varChild)) { + // doing our default action for out button is to simply click the button. + CefRefPtr browser = node_->GetBrowser(); + if (browser) { + CefMouseEvent mouse_event; + const CefRect& rect = node_->AxLocation(); + mouse_event.x = MiddleX(rect); + mouse_event.y = MiddleY(rect); + + mouse_event.modifiers = 0; + browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1); + browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1); + } + } else { + retCode = E_INVALIDARG; + } + + return retCode; +} + +// Set the name for an element in the accessibility tree +STDMETHODIMP CefIAccessible::put_accName(VARIANT varChild, BSTR szName) { + return E_NOTIMPL; +} + +// Set the value for an element in the accessibility tree +STDMETHODIMP CefIAccessible::put_accValue(VARIANT varChild, BSTR szValue) { + return E_NOTIMPL; +} + +// Return E_NOTIMPL as no help file/ topic +STDMETHODIMP CefIAccessible::get_accHelp(VARIANT varChild, BSTR* pszHelp) { + return E_NOTIMPL; +} + +STDMETHODIMP CefIAccessible::get_accHelpTopic(BSTR* pszHelpFile, + VARIANT varChild, + long* pidTopic) { + return E_NOTIMPL; +} + +// IDispatch - We are not going to return E_NOTIMPL from IDispatch methods and +// let Active Accessibility implement the IAccessible interface for them. +STDMETHODIMP CefIAccessible::GetTypeInfoCount(unsigned int FAR* pctinfo) { + return E_NOTIMPL; +} + +STDMETHODIMP CefIAccessible::GetTypeInfo(unsigned int iTInfo, + LCID lcid, + ITypeInfo FAR* FAR* ppTInfo) { + return E_NOTIMPL; +} + +STDMETHODIMP CefIAccessible::GetIDsOfNames(REFIID riid, + OLECHAR FAR* FAR* rgszNames, + unsigned int cNames, + LCID lcid, + DISPID FAR* rgDispId) { + return E_NOTIMPL; +} + +STDMETHODIMP CefIAccessible::Invoke(DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pDispParams, + VARIANT FAR* pVarResult, + EXCEPINFO FAR* pExcepInfo, + unsigned int FAR* puArgErr) { + return E_NOTIMPL; +} + +void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const { + if (event_type == "focus") { + // Notify Screen Reader of focus change + ::NotifyWinEvent(EVENT_OBJECT_FOCUS, GetWindowHandle(), OBJID_CLIENT, + node_id_); + } +} + +void OsrAXNode::Destroy() { + CefIAccessible* ptr = static_cast(platform_accessibility_); + if (ptr) + ptr->MarkDestroyed(); + platform_accessibility_ = nullptr; +} + +// Create and return NSAccessibility Implementation Object for Window +CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) { + if (!platform_accessibility_) { + platform_accessibility_ = new CefIAccessible(this); + platform_accessibility_->AddRef(); + SetParent(parent); + } + return platform_accessibility_; +} + + +#else // !defined(CEF_USE_ATL) + +void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {} + +void OsrAXNode::Destroy() {} + +CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) { + return nullptr; +} + +#endif // !defined(CEF_USE_ATL) diff --git a/src/CEF/OsrD3D11Win.cpp b/src/CEF/OsrD3D11Win.cpp new file mode 100644 index 0000000..a4d6503 --- /dev/null +++ b/src/CEF/OsrD3D11Win.cpp @@ -0,0 +1,926 @@ +// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. +// +// Portions Copyright (c) 2018 Daktronics with the following MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +#include "CEF/OsrD3D11Win.h" + +#include // For std::setw. + +#if OS_WIN && ARCH_CPU_ARM_FAMILY +#define __prefetch(x) x +#endif +#include +#include + +#include "include/base/cef_logging.h" +#include "include/internal/cef_string.h" +#include "CEF/UtilWin.h" + +// Wrap a raw COM pointer in a shared_ptr for auto Release(). +namespace d3d11 { +template +std::shared_ptr to_com_ptr(T* obj) { + return std::shared_ptr(obj, [](T* p) { + if (p) + p->Release(); + }); +} + +struct SimpleVertex { + DirectX::XMFLOAT3 pos; + DirectX::XMFLOAT2 tex; +}; + +Context::Context(ID3D11DeviceContext* ctx) : ctx_(to_com_ptr(ctx)) {} + +void Context::flush() { + ctx_->Flush(); +} + +SwapChain::SwapChain(IDXGISwapChain* swapchain, + ID3D11RenderTargetView* rtv, + ID3D11SamplerState* sampler, + ID3D11BlendState* blender) + : sampler_(to_com_ptr(sampler)), + blender_(to_com_ptr(blender)), + swapchain_(to_com_ptr(swapchain)), + rtv_(to_com_ptr(rtv)), + width_(0), + height_(0) {} + +void SwapChain::bind(const std::shared_ptr& ctx) { + ctx_ = ctx; + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + + ID3D11RenderTargetView* rtv[1] = {rtv_.get()}; + d3d11_ctx->OMSetRenderTargets(1, rtv, nullptr); + + // Set default blending state. + if (blender_) { + float factor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + d3d11_ctx->OMSetBlendState(blender_.get(), factor, 0xffffffff); + } + + // Set default sampler state. + if (sampler_) { + ID3D11SamplerState* samplers[1] = {sampler_.get()}; + d3d11_ctx->PSSetSamplers(0, 1, samplers); + } +} + +void SwapChain::unbind() { + ctx_.reset(); +} + +void SwapChain::clear(float red, float green, float blue, float alpha) { + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + CHECK(d3d11_ctx); + + FLOAT color[4] = {red, green, blue, alpha}; + d3d11_ctx->ClearRenderTargetView(rtv_.get(), color); +} + +void SwapChain::present(int sync_interval) { + swapchain_->Present(sync_interval, 0); +} + +void SwapChain::resize(int width, int height) { + if (width <= 0 || height <= 0 || width == width_ || height == height_) { + return; + } + width_ = width; + height_ = height; + + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + CHECK(d3d11_ctx); + + d3d11_ctx->OMSetRenderTargets(0, 0, 0); + rtv_.reset(); + + DXGI_SWAP_CHAIN_DESC desc; + swapchain_->GetDesc(&desc); + auto hr = swapchain_->ResizeBuffers(0, width, height, desc.BufferDesc.Format, + desc.Flags); + if (FAILED(hr)) { + LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x" + << height << ")"; + return; + } + + ID3D11Texture2D* buffer = nullptr; + hr = swapchain_->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&buffer); + if (FAILED(hr)) { + LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x" + << height << ")"; + return; + } + + ID3D11Device* dev = nullptr; + d3d11_ctx->GetDevice(&dev); + if (dev) { + D3D11_RENDER_TARGET_VIEW_DESC vdesc = {}; + vdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + vdesc.Texture2D.MipSlice = 0; + vdesc.Format = desc.BufferDesc.Format; + + ID3D11RenderTargetView* view = nullptr; + hr = dev->CreateRenderTargetView(buffer, &vdesc, &view); + if (SUCCEEDED(hr)) { + rtv_ = to_com_ptr(view); + d3d11_ctx->OMSetRenderTargets(1, &view, nullptr); + } + dev->Release(); + } + buffer->Release(); + + D3D11_VIEWPORT vp; + vp.Width = static_cast(width); + vp.Height = static_cast(height); + vp.MinDepth = D3D11_MIN_DEPTH; + vp.MaxDepth = D3D11_MAX_DEPTH; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + d3d11_ctx->RSSetViewports(1, &vp); +} + +Effect::Effect(ID3D11VertexShader* vsh, + ID3D11PixelShader* psh, + ID3D11InputLayout* layout) + : vsh_(to_com_ptr(vsh)), + psh_(to_com_ptr(psh)), + layout_(to_com_ptr(layout)) {} + +void Effect::bind(const std::shared_ptr& ctx) { + ctx_ = ctx; + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + + d3d11_ctx->IASetInputLayout(layout_.get()); + d3d11_ctx->VSSetShader(vsh_.get(), nullptr, 0); + d3d11_ctx->PSSetShader(psh_.get(), nullptr, 0); +} + +void Effect::unbind() {} + +Geometry::Geometry(D3D_PRIMITIVE_TOPOLOGY primitive, + uint32_t vertices, + uint32_t stride, + ID3D11Buffer* buffer) + : primitive_(primitive), + vertices_(vertices), + stride_(stride), + buffer_(to_com_ptr(buffer)) {} + +void Geometry::bind(const std::shared_ptr& ctx) { + ctx_ = ctx; + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + + // TODO: Handle offset. + uint32_t offset = 0; + + ID3D11Buffer* buffers[1] = {buffer_.get()}; + d3d11_ctx->IASetVertexBuffers(0, 1, buffers, &stride_, &offset); + d3d11_ctx->IASetPrimitiveTopology(primitive_); +} + +void Geometry::unbind() {} + +void Geometry::draw() { + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + CHECK(d3d11_ctx); + + // TODO: Handle offset. + d3d11_ctx->Draw(vertices_, 0); +} + +Texture2D::Texture2D(ID3D11Texture2D* tex, ID3D11ShaderResourceView* srv) + : texture_(to_com_ptr(tex)), srv_(to_com_ptr(srv)) { + share_handle_ = nullptr; + + IDXGIResource* res = nullptr; + if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIResource), + reinterpret_cast(&res)))) { + res->GetSharedHandle(&share_handle_); + res->Release(); + } + + // Are we using a keyed mutex? + IDXGIKeyedMutex* mutex = nullptr; + if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIKeyedMutex), + (void**)&mutex))) { + keyed_mutex_ = to_com_ptr(mutex); + } +} + +uint32_t Texture2D::width() const { + D3D11_TEXTURE2D_DESC desc; + texture_->GetDesc(&desc); + return desc.Width; +} + +uint32_t Texture2D::height() const { + D3D11_TEXTURE2D_DESC desc; + texture_->GetDesc(&desc); + return desc.Height; +} + +DXGI_FORMAT Texture2D::format() const { + D3D11_TEXTURE2D_DESC desc; + texture_->GetDesc(&desc); + return desc.Format; +} + +bool Texture2D::has_mutex() const { + return (keyed_mutex_.get() != nullptr); +} + +bool Texture2D::lock_key(uint64_t key, uint32_t timeout_ms) { + if (keyed_mutex_) { + const auto hr = keyed_mutex_->AcquireSync(key, timeout_ms); + return (hr == S_OK); + } + return true; +} + +void Texture2D::unlock_key(uint64_t key) { + if (keyed_mutex_) { + keyed_mutex_->ReleaseSync(key); + } +} + +void Texture2D::bind(const std::shared_ptr& ctx) { + ctx_ = ctx; + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + if (srv_) { + ID3D11ShaderResourceView* views[1] = {srv_.get()}; + d3d11_ctx->PSSetShaderResources(0, 1, views); + } +} + +void Texture2D::unbind() {} + +void* Texture2D::share_handle() const { + return share_handle_; +} + +void Texture2D::copy_from(const std::shared_ptr& other) { + ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_); + CHECK(d3d11_ctx); + if (other) { + d3d11_ctx->CopyResource(texture_.get(), other->texture_.get()); + } +} + +Device::Device(ID3D11Device* pdev, ID3D11DeviceContext* pctx) + : device_(to_com_ptr(pdev)), ctx_(std::make_shared(pctx)) { + lib_compiler_ = LoadLibrary(L"d3dcompiler_47.dll"); +} + +// static +std::shared_ptr Device::create() { + UINT flags = 0; +#ifdef _DEBUG + flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + 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, + }; + UINT num_feature_levels = sizeof(feature_levels) / sizeof(feature_levels[0]); + + ID3D11Device* pdev = nullptr; + ID3D11DeviceContext* pctx = nullptr; + + D3D_FEATURE_LEVEL selected_level; + HRESULT hr = D3D11CreateDevice( + nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, feature_levels, + num_feature_levels, D3D11_SDK_VERSION, &pdev, &selected_level, &pctx); + + if (hr == E_INVALIDARG) { + // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 + // so we need to retry without it. + hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, + &feature_levels[1], num_feature_levels - 1, + D3D11_SDK_VERSION, &pdev, &selected_level, &pctx); + } + + if (SUCCEEDED(hr)) { + const auto dev = std::make_shared(pdev, pctx); + + LOG(INFO) << "d3d11: Selected adapter " << dev->adapter_name() + << " and feature level 0x" << std::setw(4) << std::hex + << selected_level; + + return dev; + } + + return nullptr; +} + +std::string Device::adapter_name() const { + IDXGIDevice* dxgi_dev = nullptr; + auto hr = device_->QueryInterface(__uuidof(dxgi_dev), (void**)&dxgi_dev); + if (SUCCEEDED(hr)) { + IDXGIAdapter* dxgi_adapt = nullptr; + hr = dxgi_dev->GetAdapter(&dxgi_adapt); + dxgi_dev->Release(); + if (SUCCEEDED(hr)) { + DXGI_ADAPTER_DESC desc; + hr = dxgi_adapt->GetDesc(&desc); + dxgi_adapt->Release(); + if (SUCCEEDED(hr)) { + return CefString(desc.Description); + } + } + } + + return "n/a"; +} + +std::shared_ptr Device::immedidate_context() { + return ctx_; +} + +std::shared_ptr Device::create_swapchain(HWND window, + int width, + int height) { + HRESULT hr; + IDXGIFactory1* dxgi_factory = nullptr; + + // Default size to the window size unless specified. + RECT rc_bounds; + GetClientRect(window, &rc_bounds); + if (width <= 0) { + width = rc_bounds.right - rc_bounds.left; + } + if (height <= 0) { + height = rc_bounds.bottom - rc_bounds.top; + } + + { + IDXGIDevice* dxgi_dev = nullptr; + hr = device_->QueryInterface(__uuidof(IDXGIDevice), + reinterpret_cast(&dxgi_dev)); + if (FAILED(hr)) { + return nullptr; + } + + IDXGIAdapter* adapter = nullptr; + hr = dxgi_dev->GetAdapter(&adapter); + dxgi_dev->Release(); + if (FAILED(hr)) { + return nullptr; + } + + hr = adapter->GetParent(__uuidof(IDXGIFactory1), + reinterpret_cast(&dxgi_factory)); + adapter->Release(); + } + + if (!dxgi_factory) { + return nullptr; + } + + IDXGISwapChain* swapchain = nullptr; + + // Create swap chain. + IDXGIFactory2* dxgi_factory2 = nullptr; + hr = dxgi_factory->QueryInterface(__uuidof(IDXGIFactory2), + reinterpret_cast(&dxgi_factory2)); + if (dxgi_factory2) { + DXGI_SWAP_CHAIN_DESC1 sd; + ZeroMemory(&sd, sizeof(sd)); + sd.Width = width; + sd.Height = height; + sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + + IDXGISwapChain1* swapchain1 = nullptr; + hr = dxgi_factory2->CreateSwapChainForHwnd(device_.get(), window, &sd, + nullptr, nullptr, &swapchain1); + if (SUCCEEDED(hr)) { + hr = swapchain1->QueryInterface(__uuidof(IDXGISwapChain), + reinterpret_cast(&swapchain)); + swapchain1->Release(); + } + + dxgi_factory2->Release(); + } else { + // DirectX 11.0 systems. + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferCount = 1; + sd.BufferDesc.Width = width; + sd.BufferDesc.Height = height; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = window; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.Windowed = TRUE; + + hr = dxgi_factory->CreateSwapChain(device_.get(), &sd, &swapchain); + } + + // We don't handle full-screen swapchains so we block the ALT+ENTER shortcut. + dxgi_factory->MakeWindowAssociation(window, DXGI_MWA_NO_ALT_ENTER); + dxgi_factory->Release(); + + if (!swapchain) { + return nullptr; + } + + ID3D11Texture2D* back_buffer = nullptr; + hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), + reinterpret_cast(&back_buffer)); + if (FAILED(hr)) { + swapchain->Release(); + return nullptr; + } + + ID3D11RenderTargetView* rtv = nullptr; + hr = device_->CreateRenderTargetView(back_buffer, nullptr, &rtv); + back_buffer->Release(); + if (FAILED(hr)) { + swapchain->Release(); + return nullptr; + } + + const auto ctx = (ID3D11DeviceContext*)(*ctx_); + + ctx->OMSetRenderTargets(1, &rtv, nullptr); + + // Setup the viewport. + D3D11_VIEWPORT vp; + vp.Width = (FLOAT)width; + vp.Height = (FLOAT)height; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + ctx->RSSetViewports(1, &vp); + + // Create a default sampler to use. + ID3D11SamplerState* sampler = nullptr; + { + D3D11_SAMPLER_DESC desc = {}; + desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + desc.MinLOD = 0.0f; + desc.MaxLOD = D3D11_FLOAT32_MAX; + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + device_->CreateSamplerState(&desc, &sampler); + } + + // Create a default blend state to use (pre-multiplied alpha). + ID3D11BlendState* blender = nullptr; + { + D3D11_BLEND_DESC desc; + desc.AlphaToCoverageEnable = FALSE; + desc.IndependentBlendEnable = FALSE; + const auto count = sizeof(desc.RenderTarget) / sizeof(desc.RenderTarget[0]); + for (size_t n = 0; n < count; ++n) { + desc.RenderTarget[n].BlendEnable = TRUE; + desc.RenderTarget[n].SrcBlend = D3D11_BLEND_ONE; + desc.RenderTarget[n].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[n].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[n].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[n].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[n].BlendOpAlpha = D3D11_BLEND_OP_ADD; + desc.RenderTarget[n].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + } + device_->CreateBlendState(&desc, &blender); + } + + return std::make_shared(swapchain, rtv, sampler, blender); +} + +std::shared_ptr Device::create_quad(float x, + float y, + float width, + float height, + bool flip) { + x = (x * 2.0f) - 1.0f; + y = 1.0f - (y * 2.0f); + width = width * 2.0f; + height = height * 2.0f; + float z = 1.0f; + + SimpleVertex vertices[] = { + + {DirectX::XMFLOAT3(x, y, z), DirectX::XMFLOAT2(0.0f, 0.0f)}, + {DirectX::XMFLOAT3(x + width, y, z), DirectX::XMFLOAT2(1.0f, 0.0f)}, + {DirectX::XMFLOAT3(x, y - height, z), DirectX::XMFLOAT2(0.0f, 1.0f)}, + {DirectX::XMFLOAT3(x + width, y - height, z), + DirectX::XMFLOAT2(1.0f, 1.0f)}}; + + if (flip) { + DirectX::XMFLOAT2 tmp(vertices[2].tex); + vertices[2].tex = vertices[0].tex; + vertices[0].tex = tmp; + + tmp = vertices[3].tex; + vertices[3].tex = vertices[1].tex; + vertices[1].tex = tmp; + } + + D3D11_BUFFER_DESC desc = {}; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.ByteWidth = sizeof(SimpleVertex) * 4; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA srd = {}; + srd.pSysMem = vertices; + + ID3D11Buffer* buffer = nullptr; + const auto hr = device_->CreateBuffer(&desc, &srd, &buffer); + if (SUCCEEDED(hr)) { + return std::make_shared( + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, 4, + static_cast(sizeof(SimpleVertex)), buffer); + } + + return nullptr; +} + +std::shared_ptr Device::open_shared_texture(void* handle) { + ID3D11Texture2D* tex = nullptr; + auto hr = device_->OpenSharedResource(handle, __uuidof(ID3D11Texture2D), + (void**)(&tex)); + if (FAILED(hr)) { + return nullptr; + } + + D3D11_TEXTURE2D_DESC td; + tex->GetDesc(&td); + + ID3D11ShaderResourceView* srv = nullptr; + + if (td.BindFlags & D3D11_BIND_SHADER_RESOURCE) { + D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc; + srv_desc.Format = td.Format; + srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MostDetailedMip = 0; + srv_desc.Texture2D.MipLevels = 1; + + hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv); + if (FAILED(hr)) { + tex->Release(); + return nullptr; + } + } + + return std::make_shared(tex, srv); +} + +std::shared_ptr Device::create_texture(int width, + int height, + DXGI_FORMAT format, + const void* data, + size_t row_stride) { + D3D11_TEXTURE2D_DESC td; + td.ArraySize = 1; + td.BindFlags = D3D11_BIND_SHADER_RESOURCE; + td.CPUAccessFlags = 0; + td.Format = format; + td.Width = width; + td.Height = height; + td.MipLevels = 1; + td.MiscFlags = 0; + td.SampleDesc.Count = 1; + td.SampleDesc.Quality = 0; + td.Usage = D3D11_USAGE_DEFAULT; + + D3D11_SUBRESOURCE_DATA srd; + srd.pSysMem = data; + srd.SysMemPitch = static_cast(row_stride); + srd.SysMemSlicePitch = 0; + + ID3D11Texture2D* tex = nullptr; + auto hr = device_->CreateTexture2D(&td, data ? &srd : nullptr, &tex); + if (FAILED(hr)) { + return nullptr; + } + + D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc; + srv_desc.Format = td.Format; + srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MostDetailedMip = 0; + srv_desc.Texture2D.MipLevels = 1; + + ID3D11ShaderResourceView* srv = nullptr; + hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv); + if (FAILED(hr)) { + tex->Release(); + return nullptr; + } + + return std::make_shared(tex, srv); +} + +std::shared_ptr Device::compile_shader(const std::string& source_code, + const std::string& entry_point, + const std::string& model) { + if (!lib_compiler_) { + return nullptr; + } + + typedef HRESULT(WINAPI * PFN_D3DCOMPILE)( + LPCVOID, SIZE_T, LPCSTR, const D3D_SHADER_MACRO*, ID3DInclude*, LPCSTR, + LPCSTR, UINT, UINT, ID3DBlob**, ID3DBlob**); + + const auto fnc_compile = reinterpret_cast( + GetProcAddress(lib_compiler_, "D3DCompile")); + if (!fnc_compile) { + return nullptr; + } + + DWORD flags = D3DCOMPILE_ENABLE_STRICTNESS; + +#if defined(NDEBUG) +// flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; +// flags |= D3DCOMPILE_AVOID_FLOW_CONTROL; +#else + flags |= D3DCOMPILE_DEBUG; + flags |= D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + ID3DBlob* blob = nullptr; + ID3DBlob* blob_err = nullptr; + + const auto psrc = source_code.c_str(); + const auto len = source_code.size() + 1; + + const auto hr = + fnc_compile(psrc, len, nullptr, nullptr, nullptr, entry_point.c_str(), + model.c_str(), flags, 0, &blob, &blob_err); + + if (FAILED(hr)) { + if (blob_err) { + // TODO: Log the error. + blob_err->Release(); + } + return nullptr; + } + + if (blob_err) { + blob_err->Release(); + } + + return std::shared_ptr(blob, [](ID3DBlob* p) { + if (p) + p->Release(); + }); +} + +std::shared_ptr Device::create_default_effect() { + const auto vsh = + R"--(struct VS_INPUT +{ + float4 pos : POSITION; + float2 tex : TEXCOORD0; +}; + +struct VS_OUTPUT +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; +}; + +VS_OUTPUT main(VS_INPUT input) +{ + VS_OUTPUT output; + output.pos = input.pos; + output.tex = input.tex; + return output; +})--"; + + const auto psh = + R"--(Texture2D tex0 : register(t0); +SamplerState samp0 : register(s0); + +struct VS_OUTPUT +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; +}; + +float4 main(VS_OUTPUT input) : SV_Target +{ + return tex0.Sample(samp0, input.tex); +})--"; + + return create_effect(vsh, "main", "vs_4_0", psh, "main", "ps_4_0"); +} + +std::shared_ptr Device::create_effect(const std::string& vertex_code, + const std::string& vertex_entry, + const std::string& vertex_model, + const std::string& pixel_code, + const std::string& pixel_entry, + const std::string& pixel_model) { + const auto vs_blob = compile_shader(vertex_code, vertex_entry, vertex_model); + + ID3D11VertexShader* vshdr = nullptr; + ID3D11InputLayout* layout = nullptr; + + if (vs_blob) { + device_->CreateVertexShader(vs_blob->GetBufferPointer(), + vs_blob->GetBufferSize(), nullptr, &vshdr); + + D3D11_INPUT_ELEMENT_DESC layout_desc[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + + UINT elements = ARRAYSIZE(layout_desc); + + // Create the input layout. + device_->CreateInputLayout(layout_desc, elements, + vs_blob->GetBufferPointer(), + vs_blob->GetBufferSize(), &layout); + } + + const auto ps_blob = compile_shader(pixel_code, pixel_entry, pixel_model); + ID3D11PixelShader* pshdr = nullptr; + if (ps_blob) { + device_->CreatePixelShader(ps_blob->GetBufferPointer(), + ps_blob->GetBufferSize(), nullptr, &pshdr); + } + + return std::make_shared(vshdr, pshdr, layout); +} + +Layer::Layer(const std::shared_ptr& device, bool flip) + : device_(device), flip_(flip) { + bounds_.x = bounds_.y = bounds_.width = bounds_.height = 0.0f; +} + +Layer::~Layer() {} + +void Layer::attach(const std::shared_ptr& parent) { + composition_ = parent; +} + +std::shared_ptr Layer::composition() const { + return composition_.lock(); +} + +Rect Layer::bounds() const { + return bounds_; +} + +void Layer::move(float x, float y, float width, float height) { + bounds_.x = x; + bounds_.y = y; + bounds_.width = width; + bounds_.height = height; + + // It's not efficient to create the quad everytime we move, but for now we're + // just trying to get something on-screen. + geometry_.reset(); +} + +void Layer::tick(double) { + // Nothing to update in the base class. +} + +void Layer::render_texture(const std::shared_ptr& ctx, + const std::shared_ptr& texture) { + if (!geometry_) { + geometry_ = device_->create_quad(bounds_.x, bounds_.y, bounds_.width, + bounds_.height, flip_); + } + + if (geometry_ && texture) { + // We need a shader. + if (!effect_) { + effect_ = device_->create_default_effect(); + } + + // Bind our states/resource to the pipeline. + ScopedBinder quad_binder(ctx, geometry_); + ScopedBinder fx_binder(ctx, effect_); + ScopedBinder tex_binder(ctx, texture); + + // Draw the quad. + geometry_->draw(); + } +} + +Composition::Composition(const std::shared_ptr& device, + int width, + int height) + : width_(width), height_(height), vsync_(true), device_(device) { + fps_ = 0.0; + time_ = 0.0; + frame_ = 0; + fps_start_ = GetTimeNow(); +} + +bool Composition::is_vsync() const { + return vsync_; +} + +double Composition::time() const { + return time_; +} + +double Composition::fps() const { + return fps_; +} + +void Composition::add_layer(const std::shared_ptr& layer) { + if (layer) { + layers_.push_back(layer); + + // Attach ourselves as the parent. + layer->attach(shared_from_this()); + } +} + +bool Composition::remove_layer(const std::shared_ptr& layer) { + size_t match = 0; + if (layer) { + for (auto i = layers_.begin(); i != layers_.end();) { + if ((*i).get() == layer.get()) { + i = layers_.erase(i); + match++; + } else { + i++; + } + } + } + return (match > 0); +} + +void Composition::resize(bool vsync, int width, int height) { + vsync_ = vsync; + width_ = width; + height_ = height; +} + +void Composition::tick(double t) { + time_ = t; + for (const auto& layer : layers_) { + layer->tick(t); + } +} + +void Composition::render(const std::shared_ptr& ctx) { + // Use painter's algorithm and render our layers in order (not doing any dept + // or 3D here). + for (const auto& layer : layers_) { + layer->render(ctx); + } + + frame_++; + const auto now = GetTimeNow(); + if ((now - fps_start_) > 1000000) { + fps_ = frame_ / double((now - fps_start_) / 1000000.0); + frame_ = 0; + fps_start_ = now; + } +} + +FrameBuffer::FrameBuffer(const std::shared_ptr& device) + : device_(device) {} + +void FrameBuffer::on_paint(void* shared_handle) { + // Did the shared texture change? + if (shared_buffer_ && shared_handle != shared_buffer_->share_handle()) { + shared_buffer_.reset(); + } + + // Open the shared texture. + if (!shared_buffer_) { + shared_buffer_ = device_->open_shared_texture((void*)shared_handle); + if (!shared_buffer_) { + LOG(ERROR) << "d3d11: Could not open shared texture!"; + } + } +} +} \ No newline at end of file diff --git a/src/CEF/OsrD3D11Win.h b/src/CEF/OsrD3D11Win.h new file mode 100644 index 0000000..f1407cc --- /dev/null +++ b/src/CEF/OsrD3D11Win.h @@ -0,0 +1,322 @@ +// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. +// +// Portions Copyright (c) 2018 Daktronics with the following MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +#pragma once + +#include + +#include +#include +#include + +#include "include/base/cef_macros.h" + +namespace d3d11 { +class Composition; +class Context; +class Effect; +class Geometry; +class SwapChain; +class Texture2D; + + // Basic rect for floats. + struct Rect { + float x; + float y; + float width; + float height; + }; + + template + class ScopedBinder { + public: + ScopedBinder(const std::shared_ptr& ctx, + const std::shared_ptr& target) + : target_(target) { + if (target_) { + target_->bind(ctx); + } + } + ~ScopedBinder() { + if (target_) { + target_->unbind(); + } + } + + private: + const std::shared_ptr target_; + + DISALLOW_COPY_AND_ASSIGN(ScopedBinder); + }; + + class Context { + public: + Context(ID3D11DeviceContext*); + + void flush(); + + operator ID3D11DeviceContext*() { return ctx_.get(); } + + private: + const std::shared_ptr ctx_; + }; + + // Encapsulate a D3D11 Device object. + class Device { + public: + Device(ID3D11Device*, ID3D11DeviceContext*); + + static std::shared_ptr create(); + + std::string adapter_name() const; + + operator ID3D11Device*() { return device_.get(); } + + std::shared_ptr immedidate_context(); + + std::shared_ptr create_swapchain(HWND, + int width = 0, + int height = 0); + + std::shared_ptr create_quad(float x, + float y, + float width, + float height, + bool flip = false); + + std::shared_ptr create_texture(int width, + int height, + DXGI_FORMAT format, + const void* data, + size_t row_stride); + + std::shared_ptr open_shared_texture(void*); + + // Create some basic shaders so we can draw a textured-quad. + std::shared_ptr create_default_effect(); + + std::shared_ptr create_effect(const std::string& vertex_code, + const std::string& vertex_entry, + const std::string& vertex_model, + const std::string& pixel_code, + const std::string& pixel_entry, + const std::string& pixel_model); + + private: + std::shared_ptr compile_shader(const std::string& source_code, + const std::string& entry_point, + const std::string& model); + + HMODULE lib_compiler_; + + const std::shared_ptr device_; + const std::shared_ptr ctx_; + + DISALLOW_COPY_AND_ASSIGN(Device); + }; + + // Encapsulate a DXGI swapchain for a window. + class SwapChain { + public: + SwapChain(IDXGISwapChain*, + ID3D11RenderTargetView*, + ID3D11SamplerState*, + ID3D11BlendState*); + + void bind(const std::shared_ptr& ctx); + void unbind(); + + void clear(float red, float green, float blue, float alpha); + + void present(int sync_interval); + void resize(int width, int height); + + int width() const { return width_; } + int height() const { return height_; } + + private: + const std::shared_ptr sampler_; + const std::shared_ptr blender_; + const std::shared_ptr swapchain_; + std::shared_ptr rtv_; + std::shared_ptr ctx_; + int width_; + int height_; + + DISALLOW_COPY_AND_ASSIGN(SwapChain); + }; + + class Texture2D { + public: + Texture2D(ID3D11Texture2D* tex, ID3D11ShaderResourceView* srv); + + void bind(std::shared_ptr const& ctx); + void unbind(); + + uint32_t width() const; + uint32_t height() const; + DXGI_FORMAT format() const; + + bool has_mutex() const; + + bool lock_key(uint64_t key, uint32_t timeout_ms); + void unlock_key(uint64_t key); + + void* share_handle() const; + + void copy_from(const std::shared_ptr&); + + private: + HANDLE share_handle_; + + const std::shared_ptr texture_; + const std::shared_ptr srv_; + std::shared_ptr keyed_mutex_; + std::shared_ptr ctx_; + + DISALLOW_COPY_AND_ASSIGN(Texture2D); + }; + + class Effect { + public: + Effect(ID3D11VertexShader* vsh, + ID3D11PixelShader* psh, + ID3D11InputLayout* layout); + + void bind(const std::shared_ptr& ctx); + void unbind(); + + private: + const std::shared_ptr vsh_; + const std::shared_ptr psh_; + const std::shared_ptr layout_; + std::shared_ptr ctx_; + + DISALLOW_COPY_AND_ASSIGN(Effect); + }; + + class Geometry { + public: + Geometry(D3D_PRIMITIVE_TOPOLOGY primitive, + uint32_t vertices, + uint32_t stride, + ID3D11Buffer*); + + void bind(const std::shared_ptr& ctx); + void unbind(); + + void draw(); + + private: + D3D_PRIMITIVE_TOPOLOGY primitive_; + uint32_t vertices_; + uint32_t stride_; + const std::shared_ptr buffer_; + std::shared_ptr ctx_; + + DISALLOW_COPY_AND_ASSIGN(Geometry); + }; + + // Abstraction for a 2D layer within a composition. + class Layer { + public: + Layer(const std::shared_ptr& device, bool flip); + virtual ~Layer(); + + void attach(const std::shared_ptr&); + + // Uses normalized 0-1.0 coordinates. + virtual void move(float x, float y, float width, float height); + + virtual void tick(double t); + virtual void render(const std::shared_ptr& ctx) = 0; + + Rect bounds() const; + + std::shared_ptr composition() const; + + protected: + // Helper method for derived classes to draw a textured-quad. + void render_texture(const std::shared_ptr& ctx, + const std::shared_ptr& texture); + + const std::shared_ptr device_; + const bool flip_; + + Rect bounds_; + std::shared_ptr geometry_; + std::shared_ptr effect_; + + private: + std::weak_ptr composition_; + + DISALLOW_COPY_AND_ASSIGN(Layer); + }; + + // A collection of layers. Will render 1-N layers to a D3D11 device. + class Composition : public std::enable_shared_from_this { + public: + Composition(const std::shared_ptr& device, + int width = 0, + int height = 0); + + int width() const { return width_; } + int height() const { return height_; } + + double fps() const; + double time() const; + + bool is_vsync() const; + + void tick(double); + void render(const std::shared_ptr&); + + void add_layer(const std::shared_ptr& layer); + bool remove_layer(const std::shared_ptr& layer); + void resize(bool vsync, int width, int height); + + private: + int width_; + int height_; + uint32_t frame_; + int64_t fps_start_; + double fps_; + double time_; + bool vsync_; + + const std::shared_ptr device_; + std::vector> layers_; + + DISALLOW_COPY_AND_ASSIGN(Composition); + }; + + class FrameBuffer { + public: + explicit FrameBuffer(const std::shared_ptr& device); + + // Called in response to CEF's OnAcceleratedPaint notification. + void on_paint(void* shared_handle); + + // Returns what should be considered the front buffer. + std::shared_ptr texture() const { return shared_buffer_; } + + private: + const std::shared_ptr device_; + std::shared_ptr shared_buffer_; + + DISALLOW_COPY_AND_ASSIGN(FrameBuffer); + }; +} // namespace d3d11 \ No newline at end of file diff --git a/src/CEF/OsrDragdropEvents.h b/src/CEF/OsrDragdropEvents.h new file mode 100644 index 0000000..c99ac91 --- /dev/null +++ b/src/CEF/OsrDragdropEvents.h @@ -0,0 +1,30 @@ +// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + + +#pragma once + +#include "include/cef_render_handler.h" +#include "tests/cefclient/browser/client_handler.h" + +class OsrDragEvents { + public: + virtual CefBrowserHost::DragOperationsMask OnDragEnter( + CefRefPtr drag_data, + CefMouseEvent ev, + CefBrowserHost::DragOperationsMask effect) = 0; + + virtual CefBrowserHost::DragOperationsMask OnDragOver( + CefMouseEvent ev, + CefBrowserHost::DragOperationsMask effect) = 0; + + virtual void OnDragLeave() = 0; + + virtual CefBrowserHost::DragOperationsMask OnDrop( + CefMouseEvent ev, + CefBrowserHost::DragOperationsMask effect) = 0; + + protected: + virtual ~OsrDragEvents() {} +}; diff --git a/src/CEF/OsrDragdropWin.cpp b/src/CEF/OsrDragdropWin.cpp new file mode 100644 index 0000000..de85a13 --- /dev/null +++ b/src/CEF/OsrDragdropWin.cpp @@ -0,0 +1,652 @@ +// Copyright (c) 2014 The Chromium Embedded Framework 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 "CEF/OsrDragdropWin.h" + +#if defined(CEF_USE_ATL) + +#include +#include +#include + +#include +#include + +#include "include/wrapper/cef_helpers.h" +#include "CEF/BytesWriteHandler.h" +#include "CEF/resource.h" +#include "CEF/UtilWin.h" + + +DWORD DragOperationToDropEffect(CefRenderHandler::DragOperation allowed_ops) { + DWORD effect = DROPEFFECT_NONE; + if (allowed_ops & DRAG_OPERATION_COPY) + effect |= DROPEFFECT_COPY; + if (allowed_ops & DRAG_OPERATION_LINK) + effect |= DROPEFFECT_LINK; + if (allowed_ops & DRAG_OPERATION_MOVE) + effect |= DROPEFFECT_MOVE; + return effect; +} + +CefRenderHandler::DragOperationsMask DropEffectToDragOperation(DWORD effect) { + DWORD operation = DRAG_OPERATION_NONE; + if (effect & DROPEFFECT_COPY) + operation |= DRAG_OPERATION_COPY; + if (effect & DROPEFFECT_LINK) + operation |= DRAG_OPERATION_LINK; + if (effect & DROPEFFECT_MOVE) + operation |= DRAG_OPERATION_MOVE; + return static_cast(operation); +} + +CefMouseEvent ToMouseEvent(POINTL p, DWORD key_state, HWND hWnd) { + CefMouseEvent ev; + POINT screen_point = {p.x, p.y}; + ScreenToClient(hWnd, &screen_point); + ev.x = screen_point.x; + ev.y = screen_point.y; + ev.modifiers = GetCefMouseModifiers(key_state); + return ev; +} + +void GetStorageForBytes(STGMEDIUM* storage, const void* data, size_t bytes) { + HANDLE handle = GlobalAlloc(GPTR, static_cast(bytes)); + if (handle) { + memcpy(handle, data, bytes); + } + + storage->hGlobal = handle; + storage->tymed = TYMED_HGLOBAL; + storage->pUnkForRelease = nullptr; +} + +template +void GetStorageForString(STGMEDIUM* stgmed, const std::basic_string& data) { + GetStorageForBytes( + stgmed, data.c_str(), + (data.size() + 1) * sizeof(typename std::basic_string::value_type)); +} + +void GetStorageForFileDescriptor(STGMEDIUM* storage, + const std::wstring& file_name) { + DCHECK(!file_name.empty()); + HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); + + FILEGROUPDESCRIPTOR* descriptor = + reinterpret_cast(hdata); + descriptor->cItems = 1; + descriptor->fgd[0].dwFlags = FD_LINKUI; + wcsncpy_s(descriptor->fgd[0].cFileName, MAX_PATH, file_name.c_str(), + std::min(file_name.size(), static_cast(MAX_PATH - 1u))); + + storage->tymed = TYMED_HGLOBAL; + storage->hGlobal = hdata; + storage->pUnkForRelease = nullptr; +} + +// Helper method for converting from text/html to MS CF_HTML. +// Documentation for the CF_HTML format is available at +// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx +std::string HtmlToCFHtml(const std::string& html, const std::string& base_url) { + if (html.empty()) + return std::string(); + +#define MAX_DIGITS 10 +#define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) +#define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" +#define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) + + static const char* header = + "Version:0.9\r\n" + "StartHTML:" NUMBER_FORMAT + "\r\n" + "EndHTML:" NUMBER_FORMAT + "\r\n" + "StartFragment:" NUMBER_FORMAT + "\r\n" + "EndFragment:" NUMBER_FORMAT "\r\n"; + static const char* source_url_prefix = "SourceURL:"; + + static const char* start_markup = "\r\n\r\n"; + static const char* end_markup = "\r\n\r\n"; + + // Calculate offsets + size_t start_html_offset = + strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4; + if (!base_url.empty()) { + start_html_offset += + strlen(source_url_prefix) + base_url.length() + 2; // Add 2 for \r\n. + } + size_t start_fragment_offset = start_html_offset + strlen(start_markup); + size_t end_fragment_offset = start_fragment_offset + html.length(); + size_t end_html_offset = end_fragment_offset + strlen(end_markup); + char raw_result[1024]; + _snprintf(raw_result, sizeof(1024), header, start_html_offset, + end_html_offset, start_fragment_offset, end_fragment_offset); + std::string result = raw_result; + if (!base_url.empty()) { + result.append(source_url_prefix); + result.append(base_url); + result.append("\r\n"); + } + result.append(start_markup); + result.append(html); + result.append(end_markup); + +#undef MAX_DIGITS +#undef MAKE_NUMBER_FORMAT_1 +#undef MAKE_NUMBER_FORMAT_2 +#undef NUMBER_FORMAT + + return result; +} + +void CFHtmlExtractMetadata(const std::string& cf_html, + std::string* base_url, + size_t* html_start, + size_t* fragment_start, + size_t* fragment_end) { + // Obtain base_url if present. + if (base_url) { + static std::string src_url_str("SourceURL:"); + size_t line_start = cf_html.find(src_url_str); + if (line_start != std::string::npos) { + size_t src_end = cf_html.find("\n", line_start); + size_t src_start = line_start + src_url_str.length(); + if (src_end != std::string::npos && src_start != std::string::npos) { + *base_url = cf_html.substr(src_start, src_end - src_start); + } + } + } + + // Find the markup between "" and "". + // If the comments cannot be found, like copying from OpenOffice Writer, + // we simply fall back to using StartFragment/EndFragment bytecount values + // to determine the fragment indexes. + std::string cf_html_lower = cf_html; + size_t markup_start = cf_html_lower.find("(atoi(cf_html.c_str() + start_fragment_start + + start_fragment_str.length())); + } + + static std::string end_fragment_str("EndFragment:"); + size_t end_fragment_start = cf_html.find(end_fragment_str); + if (end_fragment_start != std::string::npos) { + *fragment_end = static_cast(atoi( + cf_html.c_str() + end_fragment_start + end_fragment_str.length())); + } + } else { + *fragment_start = cf_html.find('>', tag_start) + 1; + size_t tag_end = cf_html.rfind("