#include "ui/gl/child_window_win.h"
#include "base/at_exit.h"
#include "base/compiler_specific.h"
#include "base/debug/alias.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/win/wrapped_window_proc.h"
#include "ui/gfx/win/hwnd_util.h"
#include "ui/gfx/win/window_impl.h"
namespace gl {
namespace {
ATOM g_window_class;
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;
}
}
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);
DCHECK_EQ(window, gfx::GetWindowUserData(window->hwnd()));
return window->hwnd();
}
static void Destroy(HWND window) {
gfx::WindowImpl* window_data =
reinterpret_cast<gfx::WindowImpl*>(gfx::GetWindowUserData(window));
if (!window_data) {
DCHECK(!window);
return;
}
DCHECK_EQ(window, window_data->hwnd());
DestroyWindow(window);
delete window_data;
}
private:
void OnClose() {}
CR_BEGIN_MSG_MAP_EX(HiddenPopupWindow)
CR_MSG_WM_CLOSE(OnClose)
CR_END_MSG_MAP()
CR_MSG_MAP_CLASS_DECLARATIONS(HiddenPopupWindow)
};
void CreateWindowsOnThread(base::WaitableEvent* event,
HWND* child_window,
HWND* parent_window) {
InitializeWindowClass();
DCHECK(g_window_class);
*parent_window = HiddenPopupWindow::Create();
const 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, 1,
1, *parent_window, nullptr, nullptr, nullptr);
if (!window) {
logging::SystemErrorCode error = logging::GetLastSystemErrorCode();
base::debug::Alias(&error);
NOTREACHED();
}
*child_window = window;
event->Signal();
}
void DestroyWindowsOnThread(HWND child_window, HWND hidden_popup_window) {
DestroyWindow(child_window);
HiddenPopupWindow::Destroy(hidden_popup_window);
}
}
class ChildWindowWin::ChildWindowThread {
public:
static ChildWindowThread* GetInstance() {
static base::NoDestructor<ChildWindowThread> instance;
return instance.get();
}
scoped_refptr<base::TaskRunner> task_runner() {
return thread_.task_runner();
}
void DestroyThread() { thread_.Stop(); }
private:
friend class base::NoDestructor<ChildWindowThread>;
ChildWindowThread() : thread_("Window owner thread") {
base::Thread::Options options(base::MessagePumpType::UI, 0);
thread_.StartWithOptions(std::move(options));
base::AtExitManager::RegisterCallback(
[](void*) { GetInstance()->DestroyThread(); }, NULL);
}
~ChildWindowThread() {
thread_.Stop();
}
base::Thread thread_;
};
ChildWindowWin::ChildWindowWin() = default;
void ChildWindowWin::Initialize() {
if (window_)
return;
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
ChildWindowThread::GetInstance()->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&CreateWindowsOnThread, &event, &window_,
&initial_parent_window_));
event.Wait();
}
ChildWindowWin::~ChildWindowWin() {
scoped_refptr<base::TaskRunner> task_runner =
ChildWindowThread::GetInstance()->task_runner();
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&DestroyWindowsOnThread, window_, initial_parent_window_));
}
void ChildWindowWin::Resize(const gfx::Size& size) {
constexpr UINT kFlags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |
SWP_NOOWNERZORDER | SWP_NOREDRAW |
SWP_NOSENDCHANGING | SWP_NOZORDER;
if (!::SetWindowPos(window_, nullptr, 0, 0, size.width(), size.height(),
kFlags)) {
DPLOG(WARNING) << "::SetWindowPos failed";
}
}
scoped_refptr<base::TaskRunner> ChildWindowWin::GetTaskRunnerForTesting() {
return ChildWindowThread::GetInstance()->task_runner();
}
}