| // Copyright 2017 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 "gpu/ipc/service/child_window_win.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/win/scoped_hdc.h" |
| #include "base/win/wrapped_window_proc.h" |
| #include "gpu/ipc/common/gpu_messages.h" |
| #include "gpu/ipc/service/gpu_channel_manager.h" |
| #include "gpu/ipc/service/gpu_channel_manager_delegate.h" |
| #include "ui/gfx/native_widget_types.h" |
| #include "ui/gfx/win/hwnd_util.h" |
| #include "ui/gfx/win/window_impl.h" |
| |
| namespace gpu { |
| |
| namespace { |
| |
| ATOM g_window_class; |
| |
| // This runs on the window owner thread. |
| void InitializeWindowClass() { |
| if (g_window_class) |
| return; |
| |
| WNDCLASSEX intermediate_class; |
| base::win::InitializeWindowClass( |
| L"Intermediate D3D Window", |
| &base::win::WrappedWindowProc<::DefWindowProc>, CS_OWNDC, 0, 0, nullptr, |
| reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), nullptr, nullptr, |
| nullptr, &intermediate_class); |
| g_window_class = RegisterClassEx(&intermediate_class); |
| if (!g_window_class) { |
| LOG(ERROR) << "RegisterClass failed."; |
| return; |
| } |
| } |
| |
| // Hidden popup window used as a parent for the child surface window. |
| // Must be created and destroyed on the thread. |
| class HiddenPopupWindow : public gfx::WindowImpl { |
| public: |
| static HWND Create() { |
| gfx::WindowImpl* window = new HiddenPopupWindow; |
| |
| window->set_window_style(WS_POPUP); |
| window->set_window_ex_style(WS_EX_TOOLWINDOW); |
| window->Init(GetDesktopWindow(), gfx::Rect()); |
| EnableWindow(window->hwnd(), FALSE); |
| // The |window| instance is now owned by the window user data. |
| DCHECK_EQ(window, gfx::GetWindowUserData(window->hwnd())); |
| return window->hwnd(); |
| } |
| |
| static void Destroy(HWND window) { |
| // This uses the fact that the window user data contains a pointer |
| // to gfx::WindowImpl instance. |
| gfx::WindowImpl* window_data = |
| reinterpret_cast<gfx::WindowImpl*>(gfx::GetWindowUserData(window)); |
| DCHECK_EQ(window, window_data->hwnd()); |
| DestroyWindow(window); |
| delete window_data; |
| } |
| |
| private: |
| // Explicitly do nothing in Close. We do this as some external apps may get a |
| // handle to this window and attempt to close it. |
| void OnClose() {} |
| |
| CR_BEGIN_MSG_MAP_EX(HiddenPopupWindow) |
| CR_MSG_WM_CLOSE(OnClose) |
| CR_END_MSG_MAP() |
| |
| CR_MSG_MAP_CLASS_DECLARATIONS(HiddenPopupWindow) |
| }; |
| |
| // This runs on the window owner thread. |
| void CreateWindowsOnThread(const gfx::Size& size, |
| base::WaitableEvent* event, |
| HWND* child_window, |
| HWND* parent_window) { |
| InitializeWindowClass(); |
| DCHECK(g_window_class); |
| |
| // Create hidden parent window on the current thread. |
| *parent_window = HiddenPopupWindow::Create(); |
| // Create child window. |
| // WS_EX_NOPARENTNOTIFY and WS_EX_LAYERED make the window transparent for |
| // input. WS_EX_NOREDIRECTIONBITMAP avoids allocating a |
| // bitmap that would otherwise be allocated with WS_EX_LAYERED, the bitmap is |
| // only necessary if using Gdi objects with the window. |
| HWND window = CreateWindowEx( |
| WS_EX_NOPARENTNOTIFY | WS_EX_LAYERED | WS_EX_TRANSPARENT | |
| WS_EX_NOREDIRECTIONBITMAP, |
| reinterpret_cast<wchar_t*>(g_window_class), L"", |
| WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, size.width(), |
| size.height(), *parent_window, nullptr, nullptr, nullptr); |
| CHECK(window); |
| *child_window = window; |
| event->Signal(); |
| } |
| |
| // This runs on the main thread after the window was destroyed on window owner |
| // thread. |
| void DestroyThread(std::unique_ptr<base::Thread> thread) { |
| thread->Stop(); |
| } |
| |
| // This runs on the window owner thread. |
| void DestroyWindowsOnThread(HWND child_window, HWND hidden_popup_window) { |
| DestroyWindow(child_window); |
| HiddenPopupWindow::Destroy(hidden_popup_window); |
| } |
| |
| } // namespace |
| |
| ChildWindowWin::ChildWindowWin( |
| base::WeakPtr<ImageTransportSurfaceDelegate> delegate, |
| HWND parent_window) |
| : parent_window_(parent_window), window_(nullptr), delegate_(delegate) {} |
| |
| bool ChildWindowWin::Initialize() { |
| if (window_) |
| return true; |
| |
| thread_ = std::make_unique<base::Thread>("Window owner thread"); |
| base::Thread::Options options(base::MessageLoop::TYPE_UI, 0); |
| thread_->StartWithOptions(options); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| RECT window_rect; |
| GetClientRect(parent_window_, &window_rect); |
| |
| thread_->task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CreateWindowsOnThread, gfx::Rect(window_rect).size(), |
| &event, &window_, &initial_parent_window_)); |
| event.Wait(); |
| |
| delegate_->DidCreateAcceleratedSurfaceChildWindow(parent_window_, window_); |
| return true; |
| } |
| |
| ChildWindowWin::~ChildWindowWin() { |
| if (thread_) { |
| scoped_refptr<base::TaskRunner> task_runner = thread_->task_runner(); |
| task_runner->PostTaskAndReply( |
| FROM_HERE, |
| base::BindOnce(&DestroyWindowsOnThread, window_, |
| initial_parent_window_), |
| base::BindOnce(&DestroyThread, base::Passed(std::move(thread_)))); |
| } |
| } |
| |
| scoped_refptr<base::TaskRunner> ChildWindowWin::GetTaskRunnerForTesting() { |
| DCHECK(thread_); |
| return thread_->task_runner(); |
| } |
| |
| } // namespace gpu |