Import Cobalt 24.master.0.1033952
diff --git a/ui/gfx/win/DEPS b/ui/gfx/win/DEPS
new file mode 100644
index 0000000..99162b4
--- /dev/null
+++ b/ui/gfx/win/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/crash",
+]
diff --git a/ui/gfx/win/OWNERS b/ui/gfx/win/OWNERS
new file mode 100644
index 0000000..6ef3395
--- /dev/null
+++ b/ui/gfx/win/OWNERS
@@ -0,0 +1,4 @@
+# For any other files, defer to ui/gfx/OWNERS.
+
+# Direct Write, fonts and related classes.
+per-file direct_write*=etienneb@chromium.org
diff --git a/ui/gfx/win/crash_id_helper.cc b/ui/gfx/win/crash_id_helper.cc
new file mode 100644
index 0000000..5cbf920
--- /dev/null
+++ b/ui/gfx/win/crash_id_helper.cc
@@ -0,0 +1,80 @@
+// Copyright 2019 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 "ui/gfx/win/crash_id_helper.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+
+namespace gfx {
+
+// static
+CrashIdHelper* CrashIdHelper::Get() {
+ static base::NoDestructor<CrashIdHelper> helper;
+ return helper.get();
+}
+
+// static
+void CrashIdHelper::RegisterMainThread(base::PlatformThreadId thread_id) {
+ main_thread_id_ = thread_id;
+}
+
+CrashIdHelper::ScopedLogger::~ScopedLogger() {
+ CrashIdHelper::Get()->OnDidProcessMessages();
+}
+
+CrashIdHelper::ScopedLogger::ScopedLogger() = default;
+
+std::unique_ptr<CrashIdHelper::ScopedLogger>
+CrashIdHelper::OnWillProcessMessages(const std::string& id) {
+ if (main_thread_id_ == base::kInvalidThreadId ||
+ base::PlatformThread::CurrentId() != main_thread_id_) {
+ return nullptr;
+ }
+
+ if (!ids_.empty())
+ was_nested_ = true;
+ ids_.push_back(id.empty() ? "unspecified" : id);
+ debugging_crash_key_.Set(CurrentCrashId());
+ // base::WrapUnique() as constructor is private.
+ return base::WrapUnique(new ScopedLogger);
+}
+
+void CrashIdHelper::OnDidProcessMessages() {
+ DCHECK(!ids_.empty());
+ ids_.pop_back();
+ if (ids_.empty()) {
+ debugging_crash_key_.Clear();
+ was_nested_ = false;
+ } else {
+ debugging_crash_key_.Set(CurrentCrashId());
+ }
+}
+
+CrashIdHelper::CrashIdHelper() = default;
+
+CrashIdHelper::~CrashIdHelper() = default;
+
+std::string CrashIdHelper::CurrentCrashId() const {
+ // This should only be called when there is at least one id.
+ DCHECK(!ids_.empty());
+ // Common case is only one id.
+ if (ids_.size() == 1) {
+ // If the size of |ids_| is 1, then the message loop is not nested. If
+ // |was_nested_| is true, it means in processing the message corresponding
+ // to ids_[0] another message was processed, resulting in nested message
+ // loops. A nested message loop can lead to reentrancy and/or problems when
+ // the stack unravels. For example, it's entirely possible that when a
+ // nested message loop completes, objects further up in the stack have been
+ // deleted. "(N)" is added to signify that a nested message loop was run at
+ // some point during the current message loop.
+ return was_nested_ ? "(N) " + ids_[0] : ids_[0];
+ }
+ return base::JoinString(ids_, ">");
+}
+
+// static
+base::PlatformThreadId CrashIdHelper::main_thread_id_ = base::kInvalidThreadId;
+
+} // namespace gfx
diff --git a/ui/gfx/win/crash_id_helper.h b/ui/gfx/win/crash_id_helper.h
new file mode 100644
index 0000000..649e203
--- /dev/null
+++ b/ui/gfx/win/crash_id_helper.h
@@ -0,0 +1,90 @@
+// Copyright 2019 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 UI_GFX_WIN_CRASH_ID_HELPER_H_
+#define UI_GFX_WIN_CRASH_ID_HELPER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/threading/platform_thread.h"
+#include "components/crash/core/common/crash_key.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+// CrashIdHelper is used to log (in crash dumps) the id(s) of the window/widget
+// currently dispatching an event. Often times crashes occur purely in ui
+// code, while the bug lies in client code. Logging an id helps better identify
+// the client code that created the window/widget.
+//
+// This class only logs ids on the thread identified by RegisterMainThread().
+//
+// Example usage:
+// {
+// auto logger = CrashIdHelper::Get()->OnWillProcessMessages(crash_id);
+// <do message processing>
+// }
+class GFX_EXPORT CrashIdHelper {
+ public:
+ static CrashIdHelper* Get();
+
+ CrashIdHelper(const CrashIdHelper&) = delete;
+ CrashIdHelper& operator=(const CrashIdHelper&) = delete;
+
+ // Registers the thread used for logging.
+ static void RegisterMainThread(base::PlatformThreadId thread_id);
+
+ // RAII style class that unregisters in the destructor.
+ class GFX_EXPORT ScopedLogger {
+ public:
+ ScopedLogger(const ScopedLogger&) = delete;
+ ScopedLogger& operator=(const ScopedLogger&) = delete;
+
+ ~ScopedLogger();
+
+ private:
+ friend class CrashIdHelper;
+ ScopedLogger();
+ };
+
+ // Adds |id| to the list of active debugging ids. When the returned object
+ // is destroyed, |id| is removed from the list of active debugging ids.
+ // Returns null if logging is not enabled on the current thread.
+ std::unique_ptr<ScopedLogger> OnWillProcessMessages(const std::string& id);
+
+ private:
+ friend base::NoDestructor<CrashIdHelper>;
+ friend class CrashIdHelperTest;
+
+ CrashIdHelper();
+ ~CrashIdHelper();
+
+ // Called from ~ScopedLogger. Removes the most recently added id.
+ void OnDidProcessMessages();
+
+ // Returns the identifier to put in the crash key.
+ std::string CurrentCrashId() const;
+
+ // Ordered list of debugging identifiers added.
+ std::vector<std::string> ids_;
+
+ // Set to true once |ids_| has more than one object, and false once |ids_| is
+ // empty. That is, this is true once processing the windows message resulted
+ // in processing another windows message (nested message loops). See comment
+ // in implementation of CurrentCrashId() as to why this is tracked.
+ bool was_nested_ = false;
+
+ // This uses the name 'widget' as this code is most commonly triggered from
+ // views, which uses the term Widget.
+ crash_reporter::CrashKeyString<128> debugging_crash_key_{"widget-id"};
+
+ static base::PlatformThreadId main_thread_id_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_CRASH_ID_HELPER_H_
diff --git a/ui/gfx/win/crash_id_helper_unittest.cc b/ui/gfx/win/crash_id_helper_unittest.cc
new file mode 100644
index 0000000..5e3e509
--- /dev/null
+++ b/ui/gfx/win/crash_id_helper_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2019 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 "ui/gfx/win/crash_id_helper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+class CrashIdHelperTest : public testing::Test {
+ public:
+ CrashIdHelperTest() = default;
+
+ CrashIdHelperTest(const CrashIdHelperTest&) = delete;
+ CrashIdHelperTest& operator=(const CrashIdHelperTest&) = delete;
+
+ ~CrashIdHelperTest() override = default;
+
+ std::string CurrentCrashId() {
+ return CrashIdHelper::Get()->CurrentCrashId();
+ }
+};
+
+// This test verifies CurrentCrashId(). Ideally this would verify at
+// crash_reporter::CrashKeyString, but that class isn't particularly test
+// friendly (and the implementation varies depending upon compile time flags).
+TEST_F(CrashIdHelperTest, Basic) {
+ CrashIdHelper::RegisterMainThread(base::PlatformThread::CurrentId());
+
+ const std::string id1 = "id";
+ {
+ auto scoper = CrashIdHelper::Get()->OnWillProcessMessages(id1);
+ EXPECT_EQ(id1, CurrentCrashId());
+ }
+
+ // No assertions for empty as CurrentCrashId() DCHECKs there is at least
+ // one id.
+
+ const std::string id2 = "id2";
+ {
+ auto scoper = CrashIdHelper::Get()->OnWillProcessMessages(id2);
+ EXPECT_EQ(id2, CurrentCrashId());
+
+ {
+ auto scoper2 = CrashIdHelper::Get()->OnWillProcessMessages(id1);
+ EXPECT_EQ("id2>id", CurrentCrashId());
+ }
+ EXPECT_EQ("(N) id2", CurrentCrashId());
+ }
+ CrashIdHelper::RegisterMainThread(base::kInvalidThreadId);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc
new file mode 100644
index 0000000..fc7356b
--- /dev/null
+++ b/ui/gfx/win/direct_write.cc
@@ -0,0 +1,176 @@
+// Copyright 2014 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 "ui/gfx/win/direct_write.h"
+
+#include <wrl/client.h>
+
+#include <string>
+
+#include "base/debug/alias.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/win/windows_version.h"
+#include "skia/ext/fontmgr_default.h"
+#include "third_party/skia/include/core/SkFontMgr.h"
+#include "third_party/skia/include/ports/SkTypeface_win.h"
+
+namespace gfx {
+namespace win {
+
+namespace {
+
+// Pointer to the global IDWriteFactory interface.
+IDWriteFactory* g_direct_write_factory = nullptr;
+
+void SetDirectWriteFactory(IDWriteFactory* factory) {
+ DCHECK(!g_direct_write_factory);
+ // We grab a reference on the DirectWrite factory. This reference is
+ // leaked, which is ok because skia leaks it as well.
+ factory->AddRef();
+ g_direct_write_factory = factory;
+}
+
+} // anonymous namespace
+
+void CreateDWriteFactory(IDWriteFactory** factory) {
+ Microsoft::WRL::ComPtr<IUnknown> factory_unknown;
+ HRESULT hr =
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
+ &factory_unknown);
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ CHECK(false);
+ return;
+ }
+ factory_unknown.CopyTo(factory);
+}
+
+void InitializeDirectWrite() {
+ static bool tried_dwrite_initialize = false;
+ DCHECK(!tried_dwrite_initialize);
+ tried_dwrite_initialize = true;
+
+ TRACE_EVENT0("fonts", "gfx::InitializeDirectWrite");
+ SCOPED_UMA_HISTOGRAM_LONG_TIMER("DirectWrite.Fonts.Gfx.InitializeTime");
+
+ Microsoft::WRL::ComPtr<IDWriteFactory> factory;
+ CreateDWriteFactory(&factory);
+ CHECK(!!factory);
+ SetDirectWriteFactory(factory.Get());
+
+ // The skia call to create a new DirectWrite font manager instance can fail
+ // if we are unable to get the system font collection from the DirectWrite
+ // factory. The GetSystemFontCollection method in the IDWriteFactory
+ // interface fails with E_INVALIDARG on certain Windows 7 gold versions
+ // (6.1.7600.*).
+ sk_sp<SkFontMgr> direct_write_font_mgr =
+ SkFontMgr_New_DirectWrite(factory.Get());
+ int iteration = 0;
+ if (!direct_write_font_mgr &&
+ base::win::GetVersion() == base::win::Version::WIN7) {
+ // Windows (win7_rtm) may fail to map the service sections
+ // (crbug.com/956064).
+ constexpr int kMaxRetries = 5;
+ constexpr base::TimeDelta kRetrySleepTime = base::Microseconds(500);
+ while (iteration < kMaxRetries) {
+ base::PlatformThread::Sleep(kRetrySleepTime);
+ direct_write_font_mgr = SkFontMgr_New_DirectWrite(factory.Get());
+ if (direct_write_font_mgr)
+ break;
+ ++iteration;
+ }
+ }
+ if (!direct_write_font_mgr)
+ iteration = -1;
+ base::UmaHistogramSparse("DirectWrite.Fonts.Gfx.InitializeLoopCount",
+ iteration);
+ // TODO(crbug.com/956064): Move to a CHECK when the cause of the crash is
+ // fixed and remove the if statement that fallback to GDI font manager.
+ DCHECK(!!direct_write_font_mgr);
+ if (!direct_write_font_mgr)
+ direct_write_font_mgr = SkFontMgr_New_GDI();
+
+ // Override the default skia font manager. This must be called before any
+ // use of the skia font manager is done (e.g. before any call to
+ // SkFontMgr::RefDefault()).
+ skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr));
+}
+
+IDWriteFactory* GetDirectWriteFactory() {
+ // Some unittests may access this accessor without any previous call to
+ // |InitializeDirectWrite|. A call to |InitializeDirectWrite| after this
+ // function being called is still invalid.
+ if (!g_direct_write_factory)
+ InitializeDirectWrite();
+ return g_direct_write_factory;
+}
+
+absl::optional<std::string> RetrieveLocalizedString(
+ IDWriteLocalizedStrings* names,
+ const std::string& locale) {
+ std::wstring locale_wide = base::UTF8ToWide(locale);
+
+ // If locale is empty, index 0 will be used. Otherwise, the locale name must
+ // be found and must exist.
+ UINT32 index = 0;
+ BOOL exists = false;
+ if (!locale.empty() &&
+ (FAILED(names->FindLocaleName(locale_wide.c_str(), &index, &exists)) ||
+ !exists)) {
+ return absl::nullopt;
+ }
+
+ // Get the string length.
+ UINT32 length = 0;
+ if (FAILED(names->GetStringLength(index, &length)))
+ return absl::nullopt;
+
+ // The output buffer length needs to be one larger to receive the NUL
+ // character.
+ std::wstring buffer;
+ buffer.resize(length + 1);
+ if (FAILED(names->GetString(index, &buffer[0], buffer.size())))
+ return absl::nullopt;
+
+ // Shrink the string to fit the actual length.
+ buffer.resize(length);
+
+ return base::WideToUTF8(buffer);
+}
+
+absl::optional<std::string> RetrieveLocalizedFontName(
+ base::StringPiece font_name,
+ const std::string& locale) {
+ Microsoft::WRL::ComPtr<IDWriteFactory> factory;
+ CreateDWriteFactory(&factory);
+
+ Microsoft::WRL::ComPtr<IDWriteFontCollection> font_collection;
+ if (FAILED(factory->GetSystemFontCollection(&font_collection))) {
+ return absl::nullopt;
+ }
+
+ UINT32 index = 0;
+ BOOL exists;
+ std::wstring font_name_wide = base::UTF8ToWide(font_name);
+ if (FAILED(font_collection->FindFamilyName(font_name_wide.c_str(), &index,
+ &exists)) ||
+ !exists) {
+ return absl::nullopt;
+ }
+
+ Microsoft::WRL::ComPtr<IDWriteFontFamily> font_family;
+ Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> family_names;
+ if (FAILED(font_collection->GetFontFamily(index, &font_family)) ||
+ FAILED(font_family->GetFamilyNames(&family_names))) {
+ return absl::nullopt;
+ }
+
+ return RetrieveLocalizedString(family_names.Get(), locale);
+}
+
+} // namespace win
+} // namespace gfx
diff --git a/ui/gfx/win/direct_write.h b/ui/gfx/win/direct_write.h
new file mode 100644
index 0000000..2cbb4a5
--- /dev/null
+++ b/ui/gfx/win/direct_write.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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 UI_GFX_WIN_DIRECT_WRITE_H_
+#define UI_GFX_WIN_DIRECT_WRITE_H_
+
+#include <dwrite.h>
+
+#include "base/strings/string_piece.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+namespace win {
+
+GFX_EXPORT void InitializeDirectWrite();
+
+// Creates a DirectWrite factory.
+GFX_EXPORT void CreateDWriteFactory(IDWriteFactory** factory);
+
+// Returns the global DirectWrite factory.
+GFX_EXPORT IDWriteFactory* GetDirectWriteFactory();
+
+// Retrieves the localized string for a given locale. If locale is empty,
+// retrieves the first element of |names|.
+GFX_EXPORT absl::optional<std::string> RetrieveLocalizedString(
+ IDWriteLocalizedStrings* names,
+ const std::string& locale);
+
+// Retrieves the localized font name for a given locale. If locale is empty,
+// retrieves the default native font name.
+GFX_EXPORT absl::optional<std::string> RetrieveLocalizedFontName(
+ base::StringPiece font_name,
+ const std::string& locale);
+
+} // namespace win
+} // namespace gfx
+
+#endif // UI_GFX_WIN_DIRECT_WRITE_H_
diff --git a/ui/gfx/win/direct_write_unittest.cc b/ui/gfx/win/direct_write_unittest.cc
new file mode 100644
index 0000000..1c136c8
--- /dev/null
+++ b/ui/gfx/win/direct_write_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2019 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 "ui/gfx/win/direct_write.h"
+
+#include "base/i18n/rtl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(DirectWrite, RetrieveLocalizedFontName) {
+ // Retrieve the en-US localized names.
+ EXPECT_EQ(gfx::win::RetrieveLocalizedFontName("MS Gothic", "en-US"),
+ "MS Gothic");
+ EXPECT_EQ(gfx::win::RetrieveLocalizedFontName("Malgun Gothic", "en-US"),
+ "Malgun Gothic");
+
+ // Retrieve the localized names.
+ EXPECT_EQ(gfx::win::RetrieveLocalizedFontName("MS Gothic", "ja-JP"),
+ "MS ゴシック");
+ EXPECT_EQ(gfx::win::RetrieveLocalizedFontName("Malgun Gothic", "ko-KR"),
+ "맑은 고딕");
+
+ // Retrieve the default font name.
+ EXPECT_EQ(gfx::win::RetrieveLocalizedFontName("MS ゴシック", ""),
+ "MS Gothic");
+ EXPECT_EQ(gfx::win::RetrieveLocalizedFontName("맑은 고딕", ""),
+ "Malgun Gothic");
+}
diff --git a/ui/gfx/win/hwnd_util.cc b/ui/gfx/win/hwnd_util.cc
new file mode 100644
index 0000000..286b766
--- /dev/null
+++ b/ui/gfx/win/hwnd_util.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/win/hwnd_util.h"
+
+#include <windows.h>
+
+#include "base/debug/gdi_debug_util_win.h"
+#include "base/logging.h"
+#include "base/notreached.h"
+#include "base/strings/string_util.h"
+#include "base/win/win_util.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+
+namespace {
+
+// Adjust the window to fit.
+void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
+ if (fit_to_monitor) {
+ // Get the monitor.
+ HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST);
+ if (hmon) {
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfo(hmon, &mi);
+ Rect window_rect(bounds);
+ Rect monitor_rect(mi.rcWork);
+ Rect new_window_rect = window_rect;
+ new_window_rect.AdjustToFit(monitor_rect);
+ if (new_window_rect != window_rect) {
+ // Window doesn't fit on monitor, move and possibly resize.
+ SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(),
+ new_window_rect.width(), new_window_rect.height(),
+ SWP_NOACTIVATE | SWP_NOZORDER);
+ return;
+ }
+ // Else fall through.
+ } else {
+ NOTREACHED() << "Unable to find default monitor";
+ // Fall through.
+ }
+ } // Else fall through.
+
+ // The window is not being fit to monitor, or the window fits on the monitor
+ // as is, or we have no monitor info; reset the bounds.
+ ::SetWindowPos(hwnd, 0, bounds.left, bounds.top,
+ bounds.right - bounds.left, bounds.bottom - bounds.top,
+ SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+// Don't inline these functions so they show up in crash reports.
+
+NOINLINE void CrashAccessDenied(DWORD last_error) {
+ LOG(FATAL) << last_error;
+}
+
+// Crash isn't one of the ones we commonly see.
+NOINLINE void CrashOther(DWORD last_error) {
+ LOG(FATAL) << last_error;
+}
+
+} // namespace
+
+std::wstring GetClassName(HWND window) {
+ // GetClassNameW will return a truncated result (properly null terminated) if
+ // the given buffer is not large enough. So, it is not possible to determine
+ // that we got the entire class name if the result is exactly equal to the
+ // size of the buffer minus one.
+ DWORD buffer_size = MAX_PATH;
+ while (true) {
+ std::wstring output;
+ DWORD size_ret = GetClassNameW(
+ window, base::WriteInto(&output, buffer_size), buffer_size);
+ if (size_ret == 0)
+ break;
+ if (size_ret < (buffer_size - 1)) {
+ output.resize(size_ret);
+ return output;
+ }
+ buffer_size *= 2;
+ }
+ return std::wstring(); // error
+}
+
+#pragma warning(push)
+#pragma warning(disable:4312 4244)
+
+WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
+ // The reason we don't return the SetwindowLongPtr() value is that it returns
+ // the orignal window procedure and not the current one. I don't know if it is
+ // a bug or an intended feature.
+ WNDPROC oldwindow_proc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
+ return oldwindow_proc;
+}
+
+void* SetWindowUserData(HWND hwnd, void* user_data) {
+ return
+ reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(user_data)));
+}
+
+void* GetWindowUserData(HWND hwnd) {
+ DWORD process_id = 0;
+ GetWindowThreadProcessId(hwnd, &process_id);
+ // A window outside the current process needs to be ignored.
+ if (process_id != ::GetCurrentProcessId())
+ return NULL;
+ return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+}
+
+#pragma warning(pop)
+
+bool DoesWindowBelongToActiveWindow(HWND window) {
+ DCHECK(window);
+ HWND top_window = ::GetAncestor(window, GA_ROOT);
+ if (!top_window)
+ return false;
+
+ HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT);
+ return (top_window == active_top_window);
+}
+
+void CenterAndSizeWindow(HWND parent,
+ HWND window,
+ const Size& pref) {
+ DCHECK(window && pref.width() > 0 && pref.height() > 0);
+
+ // Calculate the ideal bounds.
+ RECT window_bounds;
+ RECT center_bounds = {0};
+ if (parent) {
+ // If there is a parent, center over the parents bounds.
+ ::GetWindowRect(parent, ¢er_bounds);
+ }
+
+ if (::IsRectEmpty(¢er_bounds)) {
+ // No parent or no parent rect. Center over the monitor the window is on.
+ HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
+ if (monitor) {
+ MONITORINFO mi = {0};
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfo(monitor, &mi);
+ center_bounds = mi.rcWork;
+ } else {
+ NOTREACHED() << "Unable to get default monitor";
+ }
+ }
+
+ window_bounds.left = center_bounds.left;
+ if (pref.width() < (center_bounds.right - center_bounds.left)) {
+ window_bounds.left +=
+ (center_bounds.right - center_bounds.left - pref.width()) / 2;
+ }
+ window_bounds.right = window_bounds.left + pref.width();
+
+ window_bounds.top = center_bounds.top;
+ if (pref.height() < (center_bounds.bottom - center_bounds.top)) {
+ window_bounds.top +=
+ (center_bounds.bottom - center_bounds.top - pref.height()) / 2;
+ }
+ window_bounds.bottom = window_bounds.top + pref.height();
+
+ // If we're centering a child window, we are positioning in client
+ // coordinates, and as such we need to offset the target rectangle by the
+ // position of the parent window.
+ if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) {
+ DCHECK(parent && ::GetParent(window) == parent);
+ POINT topleft = { window_bounds.left, window_bounds.top };
+ ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1);
+ window_bounds.left = topleft.x;
+ window_bounds.top = topleft.y;
+ window_bounds.right = window_bounds.left + pref.width();
+ window_bounds.bottom = window_bounds.top + pref.height();
+ }
+
+ AdjustWindowToFit(window, window_bounds, !parent);
+}
+
+void CheckWindowCreated(HWND hwnd, DWORD last_error) {
+ if (!hwnd) {
+ switch (last_error) {
+ case ERROR_NOT_ENOUGH_MEMORY:
+ base::debug::CollectGDIUsageAndDie();
+ break;
+ case ERROR_ACCESS_DENIED:
+ CrashAccessDenied(last_error);
+ break;
+ default:
+ CrashOther(last_error);
+ break;
+ }
+ LOG(FATAL) << last_error;
+ }
+}
+
+extern "C" {
+ typedef HWND (*RootWindow)();
+}
+
+HWND GetWindowToParentTo(bool get_real_hwnd) {
+ return get_real_hwnd ? ::GetDesktopWindow() : HWND_DESKTOP;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/hwnd_util.h b/ui/gfx/win/hwnd_util.h
new file mode 100644
index 0000000..d4d8eaf
--- /dev/null
+++ b/ui/gfx/win/hwnd_util.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_WIN_HWND_UTIL_H_
+#define UI_GFX_WIN_HWND_UTIL_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+class Size;
+
+// A version of the GetClassNameW API that returns the class name in an
+// std::wstring. An empty result indicates a failure to get the class name.
+GFX_EXPORT std::wstring GetClassName(HWND hwnd);
+
+// Useful for subclassing a HWND. Returns the previous window procedure.
+GFX_EXPORT WNDPROC SetWindowProc(HWND hwnd, WNDPROC wndproc);
+
+// Pointer-friendly wrappers around Get/SetWindowLong(..., GWLP_USERDATA, ...)
+// Returns the previously set value.
+GFX_EXPORT void* SetWindowUserData(HWND hwnd, void* user_data);
+GFX_EXPORT void* GetWindowUserData(HWND hwnd);
+
+// Returns true if the specified window is the current active top window or one
+// of its children.
+GFX_EXPORT bool DoesWindowBelongToActiveWindow(HWND window);
+
+// Sizes the window to have a window size of |pref|, then centers the window
+// over |parent|, ensuring the window fits on screen.
+GFX_EXPORT void CenterAndSizeWindow(HWND parent,
+ HWND window,
+ const gfx::Size& pref);
+
+// If |hwnd| is nullptr logs various thing and CHECKs. |last_error| must contain
+// the result of ::GetLastError(), called immediately after CreateWindow().
+GFX_EXPORT void CheckWindowCreated(HWND hwnd, DWORD last_error);
+
+// Returns the window you can use to parent a top level window.
+// Note that in some cases we create child windows not parented to its final
+// container so in those cases you should pass true in |get_real_hwnd|.
+GFX_EXPORT HWND GetWindowToParentTo(bool get_real_hwnd);
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_HWND_UTIL_H_
diff --git a/ui/gfx/win/msg_util.h b/ui/gfx/win/msg_util.h
new file mode 100644
index 0000000..7c6b42b
--- /dev/null
+++ b/ui/gfx/win/msg_util.h
@@ -0,0 +1,2261 @@
+// Copyright 2014 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 UI_GFX_WIN_MSG_UTIL_H_
+#define UI_GFX_WIN_MSG_UTIL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+
+// Based on WTL version 8.0 atlcrack.h
+
+// This differs from the original atlcrack.h by removing usage of CPoint,
+// CSize, etc.
+
+///////////////////////////////////////////////////////////////////////////////
+// Message map macro for cracked handlers
+
+// Note about message maps with cracked handlers:
+// For ATL 3.0, a message map using cracked handlers MUST use
+// CR_BEGIN_MSG_MAP_EX. For ATL 7.0 or higher, you can use CR_BEGIN_MSG_MAP for
+// CWindowImpl/CDialogImpl derived classes, but must use CR_BEGIN_MSG_MAP_EX for
+// classes that don't derive from CWindowImpl/CDialogImpl.
+// Classes using the CR_BEGIN_MSG_MAP_EX/CR_END_MSG_MAP set of macros must
+// also include a CR_MSG_MAP_CLASS_DECLARATIONS macro after all members in
+// the class definition since the macros add a
+// base::WeakPtrFactory which is only allowed if last in the class.
+
+#define CR_BEGIN_MSG_MAP_EX(theClass) \
+ public: \
+ /* "handled" management for cracked handlers */ \
+ void SetMsgHandled(BOOL handled) { msg_handled_ = handled; } \
+ BOOL ProcessWindowMessage(HWND hwnd, UINT msg, WPARAM w_param, \
+ LPARAM l_param, LRESULT& l_result, \
+ DWORD msg_map_id = 0) override { \
+ auto ref(theClass::msg_handler_weak_factory_.GetWeakPtr()); \
+ BOOL old_msg_handled = msg_handled_; \
+ BOOL ret = _ProcessWindowMessage(hwnd, msg, w_param, l_param, l_result, \
+ msg_map_id); \
+ if (ref.get()) \
+ msg_handled_ = old_msg_handled; \
+ return ret; \
+ } \
+ BOOL _ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, \
+ LPARAM lParam, LRESULT& lResult, \
+ DWORD dwMsgMapID) { \
+ auto ref(theClass::msg_handler_weak_factory_.GetWeakPtr()); \
+ BOOL bHandled = TRUE; \
+ hWnd; \
+ uMsg; \
+ wParam; \
+ lParam; \
+ lResult; \
+ bHandled; \
+ switch (dwMsgMapID) { \
+ case 0:
+
+// Replacement for atlwin.h's END_MSG_MAP for removing ATL usage.
+#define CR_END_MSG_MAP() \
+ break; \
+ default: \
+ NOTREACHED() << "Invalid message map ID: " << dwMsgMapID; \
+ break; \
+ } \
+ return FALSE; \
+ }
+
+// This macro must be last in the class since it contains a
+// base::WeakPtrFactory which must be last in the class.
+#define CR_MSG_MAP_CLASS_DECLARATIONS(theClass) \
+ private: \
+ BOOL msg_handled_{false}; \
+ base::WeakPtrFactory<theClass> msg_handler_weak_factory_{this};
+
+#define CR_GET_X_LPARAM(lParam) ((int)(short)LOWORD(lParam))
+#define CR_GET_Y_LPARAM(lParam) ((int)(short)HIWORD(lParam))
+
+///////////////////////////////////////////////////////////////////////////////
+// Standard Windows message macros
+
+// int OnCreate(LPCREATESTRUCT lpCreateStruct)
+#define CR_MSG_WM_CREATE(func) \
+ if (uMsg == WM_CREATE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam)
+#define CR_MSG_WM_INITDIALOG(func) \
+ if (uMsg == WM_INITDIALOG) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HWND)wParam, lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnCopyData(CWindow wnd, PCOPYDATASTRUCT pCopyDataStruct)
+#define CR_MSG_WM_COPYDATA(func) \
+ if (uMsg == WM_COPYDATA) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HWND)wParam, (PCOPYDATASTRUCT)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDestroy()
+#define CR_MSG_WM_DESTROY(func) \
+ if (uMsg == WM_DESTROY) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMove(CPoint ptPos)
+#define CR_MSG_WM_MOVE(func) \
+ if (uMsg == WM_MOVE) { \
+ SetMsgHandled(TRUE); \
+ func(gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSize(UINT nType, gfx::Size size)
+#define CR_MSG_WM_SIZE(func) \
+ if (uMsg == WM_SIZE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Size(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnActivate(UINT nState, BOOL bMinimized, CWindow wndOther)
+#define CR_MSG_WM_ACTIVATE(func) \
+ if (uMsg == WM_ACTIVATE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)LOWORD(wParam), (BOOL)HIWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSetFocus(CWindow wndOld)
+#define CR_MSG_WM_SETFOCUS(func) \
+ if (uMsg == WM_SETFOCUS) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnKillFocus(CWindow wndFocus)
+#define CR_MSG_WM_KILLFOCUS(func) \
+ if (uMsg == WM_KILLFOCUS) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnEnable(BOOL bEnable)
+#define CR_MSG_WM_ENABLE(func) \
+ if (uMsg == WM_ENABLE) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPaint(CDCHandle dc)
+#define CR_MSG_WM_PAINT(func) \
+ if (uMsg == WM_PAINT) { \
+ SetMsgHandled(TRUE); \
+ func((HDC)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnClose()
+#define CR_MSG_WM_CLOSE(func) \
+ if (uMsg == WM_CLOSE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnQueryEndSession(UINT nSource, UINT uLogOff)
+#define CR_MSG_WM_QUERYENDSESSION(func) \
+ if (uMsg == WM_QUERYENDSESSION) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)wParam, (UINT)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnQueryOpen()
+#define CR_MSG_WM_QUERYOPEN(func) \
+ if (uMsg == WM_QUERYOPEN) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnEraseBkgnd(CDCHandle dc)
+#define CR_MSG_WM_ERASEBKGND(func) \
+ if (uMsg == WM_ERASEBKGND) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSysColorChange()
+#define CR_MSG_WM_SYSCOLORCHANGE(func) \
+ if (uMsg == WM_SYSCOLORCHANGE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnEndSession(BOOL bEnding, UINT uLogOff)
+#define CR_MSG_WM_ENDSESSION(func) \
+ if (uMsg == WM_ENDSESSION) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam, (UINT)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnShowWindow(BOOL bShow, UINT nStatus)
+#define CR_MSG_WM_SHOWWINDOW(func) \
+ if (uMsg == WM_SHOWWINDOW) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam, (int)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnCtlColorEdit(CDCHandle dc, CEdit edit)
+#define CR_MSG_WM_CTLCOLOREDIT(func) \
+ if (uMsg == WM_CTLCOLOREDIT) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnCtlColorListBox(CDCHandle dc, CListBox listBox)
+#define CR_MSG_WM_CTLCOLORLISTBOX(func) \
+ if (uMsg == WM_CTLCOLORLISTBOX) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnCtlColorBtn(CDCHandle dc, CButton button)
+#define CR_MSG_WM_CTLCOLORBTN(func) \
+ if (uMsg == WM_CTLCOLORBTN) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnCtlColorDlg(CDCHandle dc, CWindow wnd)
+#define CR_MSG_WM_CTLCOLORDLG(func) \
+ if (uMsg == WM_CTLCOLORDLG) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar)
+#define CR_MSG_WM_CTLCOLORSCROLLBAR(func) \
+ if (uMsg == WM_CTLCOLORSCROLLBAR) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnCtlColorStatic(CDCHandle dc, CStatic wndStatic)
+#define CR_MSG_WM_CTLCOLORSTATIC(func) \
+ if (uMsg == WM_CTLCOLORSTATIC) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
+#define CR_MSG_WM_SETTINGCHANGE(func) \
+ if (uMsg == WM_SETTINGCHANGE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPCTSTR)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDevModeChange(LPCTSTR lpDeviceName)
+#define CR_MSG_WM_DEVMODECHANGE(func) \
+ if (uMsg == WM_DEVMODECHANGE) { \
+ SetMsgHandled(TRUE); \
+ func((LPCTSTR)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnActivateApp(BOOL bActive, DWORD dwThreadID)
+#define CR_MSG_WM_ACTIVATEAPP(func) \
+ if (uMsg == WM_ACTIVATEAPP) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam, (DWORD)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnFontChange()
+#define CR_MSG_WM_FONTCHANGE(func) \
+ if (uMsg == WM_FONTCHANGE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnTimeChange()
+#define CR_MSG_WM_TIMECHANGE(func) \
+ if (uMsg == WM_TIMECHANGE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCancelMode()
+#define CR_MSG_WM_CANCELMODE(func) \
+ if (uMsg == WM_CANCELMODE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message)
+#define CR_MSG_WM_SETCURSOR(func) \
+ if (uMsg == WM_SETCURSOR) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), \
+ (UINT)HIWORD(lParam)); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnMouseActivate(CWindow wndTopLevel, UINT nHitTest, UINT message)
+#define CR_MSG_WM_MOUSEACTIVATE(func) \
+ if (uMsg == WM_MOUSEACTIVATE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), \
+ (UINT)HIWORD(lParam)); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnChildActivate()
+#define CR_MSG_WM_CHILDACTIVATE(func) \
+ if (uMsg == WM_CHILDACTIVATE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnGetMinMaxInfo(LPMINMAXINFO lpMMI)
+#define CR_MSG_WM_GETMINMAXINFO(func) \
+ if (uMsg == WM_GETMINMAXINFO) { \
+ SetMsgHandled(TRUE); \
+ func((LPMINMAXINFO)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnIconEraseBkgnd(CDCHandle dc)
+#define CR_MSG_WM_ICONERASEBKGND(func) \
+ if (uMsg == WM_ICONERASEBKGND) { \
+ SetMsgHandled(TRUE); \
+ func((HDC)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSpoolerStatus(UINT nStatus, UINT nJobs)
+#define CR_MSG_WM_SPOOLERSTATUS(func) \
+ if (uMsg == WM_SPOOLERSTATUS) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (UINT)LOWORD(lParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
+#define CR_MSG_WM_DRAWITEM(func) \
+ if (uMsg == WM_DRAWITEM) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
+#define CR_MSG_WM_MEASUREITEM(func) \
+ if (uMsg == WM_MEASUREITEM) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct)
+#define CR_MSG_WM_DELETEITEM(func) \
+ if (uMsg == WM_DELETEITEM) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnCharToItem(UINT nChar, UINT nIndex, CListBox listBox)
+#define CR_MSG_WM_CHARTOITEM(func) \
+ if (uMsg == WM_CHARTOITEM) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), \
+ (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox)
+#define CR_MSG_WM_VKEYTOITEM(func) \
+ if (uMsg == WM_VKEYTOITEM) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), \
+ (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HCURSOR OnQueryDragIcon()
+#define CR_MSG_WM_QUERYDRAGICON(func) \
+ if (uMsg == WM_QUERYDRAGICON) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct)
+#define CR_MSG_WM_COMPAREITEM(func) \
+ if (uMsg == WM_COMPAREITEM) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCompacting(UINT nCpuTime)
+#define CR_MSG_WM_COMPACTING(func) \
+ if (uMsg == WM_COMPACTING) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnNcCreate(LPCREATESTRUCT lpCreateStruct)
+#define CR_MSG_WM_NCCREATE(func) \
+ if (uMsg == WM_NCCREATE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcDestroy()
+#define CR_MSG_WM_NCDESTROY(func) \
+ if (uMsg == WM_NCDESTROY) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam)
+#define CR_MSG_WM_NCCALCSIZE(func) \
+ if (uMsg == WM_NCCALCSIZE) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((BOOL)wParam, lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// UINT OnNcHitTest(gfx::Point point)
+#define CR_MSG_WM_NCHITTEST(func) \
+ if (uMsg == WM_NCHITTEST) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func( \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcPaint(CRgn rgn)
+#define CR_MSG_WM_NCPAINT(func) \
+ if (uMsg == WM_NCPAINT) { \
+ SetMsgHandled(TRUE); \
+ func((HRGN)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnNcActivate(BOOL bActive)
+#define CR_MSG_WM_NCACTIVATE(func) \
+ if (uMsg == WM_NCACTIVATE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((BOOL)wParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// UINT OnGetDlgCode(LPMSG lpMsg)
+#define CR_MSG_WM_GETDLGCODE(func) \
+ if (uMsg == WM_GETDLGCODE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((LPMSG)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcMouseMove(UINT nHitTest, gfx::Point point)
+#define CR_MSG_WM_NCMOUSEMOVE(func) \
+ if (uMsg == WM_NCMOUSEMOVE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcLButtonDown(UINT nHitTest, gfx::Point point)
+#define CR_MSG_WM_NCLBUTTONDOWN(func) \
+ if (uMsg == WM_NCLBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcLButtonUp(UINT nHitTest, gfx::Point point)
+#define CR_MSG_WM_NCLBUTTONUP(func) \
+ if (uMsg == WM_NCLBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcLButtonDblClk(UINT nHitTest, gfx::Point point)
+#define CR_MSG_WM_NCLBUTTONDBLCLK(func) \
+ if (uMsg == WM_NCLBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcRButtonDown(UINT nHitTest, gfx::Point point)
+#define CR_MSG_WM_NCRBUTTONDOWN(func) \
+ if (uMsg == WM_NCRBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcRButtonUp(UINT nHitTest, gfx::Point point)
+#define CR_MSG_WM_NCRBUTTONUP(func) \
+ if (uMsg == WM_NCRBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcRButtonDblClk(UINT nHitTest, CPoint point)
+#define CR_MSG_WM_NCRBUTTONDBLCLK(func) \
+ if (uMsg == WM_NCRBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcMButtonDown(UINT nHitTest, CPoint point)
+#define CR_MSG_WM_NCMBUTTONDOWN(func) \
+ if (uMsg == WM_NCMBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcMButtonUp(UINT nHitTest, CPoint point)
+#define CR_MSG_WM_NCMBUTTONUP(func) \
+ if (uMsg == WM_NCMBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNcMButtonDblClk(UINT nHitTest, CPoint point)
+#define CR_MSG_WM_NCMBUTTONDBLCLK(func) \
+ if (uMsg == WM_NCMBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_KEYDOWN(func) \
+ if (uMsg == WM_KEYDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_KEYUP(func) \
+ if (uMsg == WM_KEYUP) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_CHAR(func) \
+ if (uMsg == WM_CHAR) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDeadChar(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_DEADCHAR(func) \
+ if (uMsg == WM_DEADCHAR) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_SYSKEYDOWN(func) \
+ if (uMsg == WM_SYSKEYDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_SYSKEYUP(func) \
+ if (uMsg == WM_SYSKEYUP) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_SYSCHAR(func) \
+ if (uMsg == WM_SYSCHAR) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSysDeadChar(UINT nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_SYSDEADCHAR(func) \
+ if (uMsg == WM_SYSDEADCHAR) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSysCommand(UINT nID, LPARAM lParam)
+#define CR_MSG_WM_SYSCOMMAND(func) \
+ if (uMsg == WM_SYSCOMMAND) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnTCard(UINT idAction, DWORD dwActionData)
+#define CR_MSG_WM_TCARD(func) \
+ if (uMsg == WM_TCARD) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (DWORD)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnTimer(UINT_PTR nIDEvent)
+#define CR_MSG_WM_TIMER(func) \
+ if (uMsg == WM_TIMER) { \
+ SetMsgHandled(TRUE); \
+ func((UINT_PTR)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)
+#define CR_MSG_WM_HSCROLL(func) \
+ if (uMsg == WM_HSCROLL) { \
+ SetMsgHandled(TRUE); \
+ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)
+#define CR_MSG_WM_VSCROLL(func) \
+ if (uMsg == WM_VSCROLL) { \
+ SetMsgHandled(TRUE); \
+ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnInitMenu(CMenu menu)
+#define CR_MSG_WM_INITMENU(func) \
+ if (uMsg == WM_INITMENU) { \
+ SetMsgHandled(TRUE); \
+ func((HMENU)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnInitMenuPopup(CMenu menuPopup, UINT nIndex, BOOL bSysMenu)
+#define CR_MSG_WM_INITMENUPOPUP(func) \
+ if (uMsg == WM_INITMENUPOPUP) { \
+ SetMsgHandled(TRUE); \
+ func((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMenuSelect(UINT nItemID, UINT nFlags, CMenu menu)
+#define CR_MSG_WM_MENUSELECT(func) \
+ if (uMsg == WM_MENUSELECT) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu menu)
+#define CR_MSG_WM_MENUCHAR(func) \
+ if (uMsg == WM_MENUCHAR) { \
+ SetMsgHandled(TRUE); \
+ lResult = \
+ func((TCHAR)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNotify(int idCtrl, LPNMHDR pnmh)
+#define CR_MSG_WM_NOTIFY(func) \
+ if (uMsg == WM_NOTIFY) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((int)wParam, (LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnEnterIdle(UINT nWhy, CWindow wndWho)
+#define CR_MSG_WM_ENTERIDLE(func) \
+ if (uMsg == WM_ENTERIDLE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMouseMove(UINT nFlags, CPoint point)
+#define CR_MSG_WM_MOUSEMOVE(func) \
+ if (uMsg == WM_MOUSEMOVE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
+#define CR_MSG_WM_MOUSEWHEEL(func) \
+ if (uMsg == WM_MOUSEWHEEL) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func( \
+ (UINT)LOWORD(wParam), (short)HIWORD(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnLButtonDown(UINT nFlags, CPoint point)
+#define CR_MSG_WM_LBUTTONDOWN(func) \
+ if (uMsg == WM_LBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnLButtonUp(UINT nFlags, CPoint point)
+#define CR_MSG_WM_LBUTTONUP(func) \
+ if (uMsg == WM_LBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnLButtonDblClk(UINT nFlags, CPoint point)
+#define CR_MSG_WM_LBUTTONDBLCLK(func) \
+ if (uMsg == WM_LBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnRButtonDown(UINT nFlags, CPoint point)
+#define CR_MSG_WM_RBUTTONDOWN(func) \
+ if (uMsg == WM_RBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnRButtonUp(UINT nFlags, CPoint point)
+#define CR_MSG_WM_RBUTTONUP(func) \
+ if (uMsg == WM_RBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnRButtonDblClk(UINT nFlags, CPoint point)
+#define CR_MSG_WM_RBUTTONDBLCLK(func) \
+ if (uMsg == WM_RBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMButtonDown(UINT nFlags, CPoint point)
+#define CR_MSG_WM_MBUTTONDOWN(func) \
+ if (uMsg == WM_MBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMButtonUp(UINT nFlags, CPoint point)
+#define CR_MSG_WM_MBUTTONUP(func) \
+ if (uMsg == WM_MBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMButtonDblClk(UINT nFlags, CPoint point)
+#define CR_MSG_WM_MBUTTONDBLCLK(func) \
+ if (uMsg == WM_MBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnParentNotify(UINT message, UINT nChildID, LPARAM lParam)
+#define CR_MSG_WM_PARENTNOTIFY(func) \
+ if (uMsg == WM_PARENTNOTIFY) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMDIActivate(CWindow wndActivate, CWindow wndDeactivate)
+#define CR_MSG_WM_MDIACTIVATE(func) \
+ if (uMsg == WM_MDIACTIVATE) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnRenderFormat(UINT nFormat)
+#define CR_MSG_WM_RENDERFORMAT(func) \
+ if (uMsg == WM_RENDERFORMAT) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnRenderAllFormats()
+#define CR_MSG_WM_RENDERALLFORMATS(func) \
+ if (uMsg == WM_RENDERALLFORMATS) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDestroyClipboard()
+#define CR_MSG_WM_DESTROYCLIPBOARD(func) \
+ if (uMsg == WM_DESTROYCLIPBOARD) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDrawClipboard()
+#define CR_MSG_WM_DRAWCLIPBOARD(func) \
+ if (uMsg == WM_DRAWCLIPBOARD) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPaintClipboard(CWindow wndViewer, const LPPAINTSTRUCT lpPaintStruct)
+#define CR_MSG_WM_PAINTCLIPBOARD(func) \
+ if (uMsg == WM_PAINTCLIPBOARD) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, (const LPPAINTSTRUCT)::GlobalLock((HGLOBAL)lParam)); \
+ ::GlobalUnlock((HGLOBAL)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnVScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos)
+#define CR_MSG_WM_VSCROLLCLIPBOARD(func) \
+ if (uMsg == WM_VSCROLLCLIPBOARD) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnContextMenu(CWindow wnd, CPoint point)
+#define CR_MSG_WM_CONTEXTMENU(func) \
+ if (uMsg == WM_CONTEXTMENU) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSizeClipboard(CWindow wndViewer, const LPRECT lpRect)
+#define CR_MSG_WM_SIZECLIPBOARD(func) \
+ if (uMsg == WM_SIZECLIPBOARD) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, (const LPRECT)::GlobalLock((HGLOBAL)lParam)); \
+ ::GlobalUnlock((HGLOBAL)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnAskCbFormatName(UINT nMaxCount, LPTSTR lpszString)
+#define CR_MSG_WM_ASKCBFORMATNAME(func) \
+ if (uMsg == WM_ASKCBFORMATNAME) { \
+ SetMsgHandled(TRUE); \
+ func((DWORD)wParam, (LPTSTR)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnChangeCbChain(CWindow wndRemove, CWindow wndAfter)
+#define CR_MSG_WM_CHANGECBCHAIN(func) \
+ if (uMsg == WM_CHANGECBCHAIN) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnHScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos)
+#define CR_MSG_WM_HSCROLLCLIPBOARD(func) \
+ if (uMsg == WM_HSCROLLCLIPBOARD) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnQueryNewPalette()
+#define CR_MSG_WM_QUERYNEWPALETTE(func) \
+ if (uMsg == WM_QUERYNEWPALETTE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPaletteChanged(CWindow wndFocus)
+#define CR_MSG_WM_PALETTECHANGED(func) \
+ if (uMsg == WM_PALETTECHANGED) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPaletteIsChanging(CWindow wndPalChg)
+#define CR_MSG_WM_PALETTEISCHANGING(func) \
+ if (uMsg == WM_PALETTEISCHANGING) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDropFiles(HDROP hDropInfo)
+#define CR_MSG_WM_DROPFILES(func) \
+ if (uMsg == WM_DROPFILES) { \
+ SetMsgHandled(TRUE); \
+ func((HDROP)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnWindowPosChanging(LPWINDOWPOS lpWndPos)
+#define CR_MSG_WM_WINDOWPOSCHANGING(func) \
+ if (uMsg == WM_WINDOWPOSCHANGING) { \
+ SetMsgHandled(TRUE); \
+ func((LPWINDOWPOS)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnWindowPosChanged(LPWINDOWPOS lpWndPos)
+#define CR_MSG_WM_WINDOWPOSCHANGED(func) \
+ if (uMsg == WM_WINDOWPOSCHANGED) { \
+ SetMsgHandled(TRUE); \
+ func((LPWINDOWPOS)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnExitMenuLoop(BOOL fIsTrackPopupMenu)
+#define CR_MSG_WM_EXITMENULOOP(func) \
+ if (uMsg == WM_EXITMENULOOP) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnEnterMenuLoop(BOOL fIsTrackPopupMenu)
+#define CR_MSG_WM_ENTERMENULOOP(func) \
+ if (uMsg == WM_ENTERMENULOOP) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnStyleChanged(int nStyleType, LPSTYLESTRUCT lpStyleStruct)
+#define CR_MSG_WM_STYLECHANGED(func) \
+ if (uMsg == WM_STYLECHANGED) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPSTYLESTRUCT)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct)
+#define CR_MSG_WM_STYLECHANGING(func) \
+ if (uMsg == WM_STYLECHANGING) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPSTYLESTRUCT)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSizing(UINT fwSide, LPRECT pRect)
+#define CR_MSG_WM_SIZING(func) \
+ if (uMsg == WM_SIZING) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPRECT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMoving(UINT fwSide, LPRECT pRect)
+#define CR_MSG_WM_MOVING(func) \
+ if (uMsg == WM_MOVING) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPRECT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCaptureChanged(CWindow wnd)
+#define CR_MSG_WM_CAPTURECHANGED(func) \
+ if (uMsg == WM_CAPTURECHANGED) { \
+ SetMsgHandled(TRUE); \
+ func((HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnDeviceChange(UINT nEventType, DWORD dwData)
+#define CR_MSG_WM_DEVICECHANGE(func) \
+ if (uMsg == WM_DEVICECHANGE) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)wParam, (DWORD)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define CR_MSG_WM_COMMAND(func) \
+ if (uMsg == WM_COMMAND) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDisplayChange(UINT uBitsPerPixel, gfx::Size sizeScreen)
+#define CR_MSG_WM_DISPLAYCHANGE(func) \
+ if (uMsg == WM_DISPLAYCHANGE) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, \
+ gfx::Size(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnEnterSizeMove()
+#define CR_MSG_WM_ENTERSIZEMOVE(func) \
+ if (uMsg == WM_ENTERSIZEMOVE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnExitSizeMove()
+#define CR_MSG_WM_EXITSIZEMOVE(func) \
+ if (uMsg == WM_EXITSIZEMOVE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HFONT OnGetFont()
+#define CR_MSG_WM_GETFONT(func) \
+ if (uMsg == WM_GETFONT) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnGetHotKey()
+#define CR_MSG_WM_GETHOTKEY(func) \
+ if (uMsg == WM_GETHOTKEY) { \
+ SetMsgHandled(TRUE); \
+ lResult = func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HICON OnGetIcon()
+#define CR_MSG_WM_GETICON(func) \
+ if (uMsg == WM_GETICON) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)wParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnGetText(int cchTextMax, LPTSTR lpszText)
+#define CR_MSG_WM_GETTEXT(func) \
+ if (uMsg == WM_GETTEXT) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((int)wParam, (LPTSTR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnGetTextLength()
+#define CR_MSG_WM_GETTEXTLENGTH(func) \
+ if (uMsg == WM_GETTEXTLENGTH) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnHelp(LPHELPINFO lpHelpInfo)
+#define CR_MSG_WM_HELP(func) \
+ if (uMsg == WM_HELP) { \
+ SetMsgHandled(TRUE); \
+ func((LPHELPINFO)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnHotKey(int nHotKeyID, UINT uModifiers, UINT uVirtKey)
+#define CR_MSG_WM_HOTKEY(func) \
+ if (uMsg == WM_HOTKEY) { \
+ SetMsgHandled(TRUE); \
+ func((int)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnInputLangChange(DWORD dwCharSet, HKL hKbdLayout)
+#define CR_MSG_WM_INPUTLANGCHANGE(func) \
+ if (uMsg == WM_INPUTLANGCHANGE) { \
+ SetMsgHandled(TRUE); \
+ func((DWORD)wParam, (HKL)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnInputLangChangeRequest(BOOL bSysCharSet, HKL hKbdLayout)
+#define CR_MSG_WM_INPUTLANGCHANGEREQUEST(func) \
+ if (uMsg == WM_INPUTLANGCHANGEREQUEST) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam, (HKL)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNextDlgCtl(BOOL bHandle, WPARAM wCtlFocus)
+#define CR_MSG_WM_NEXTDLGCTL(func) \
+ if (uMsg == WM_NEXTDLGCTL) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)LOWORD(lParam), wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNextMenu(int nVirtKey, LPMDINEXTMENU lpMdiNextMenu)
+#define CR_MSG_WM_NEXTMENU(func) \
+ if (uMsg == WM_NEXTMENU) { \
+ SetMsgHandled(TRUE); \
+ func((int)wParam, (LPMDINEXTMENU)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnNotifyFormat(CWindow wndFrom, int nCommand)
+#define CR_MSG_WM_NOTIFYFORMAT(func) \
+ if (uMsg == WM_NOTIFYFORMAT) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HWND)wParam, (int)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// BOOL OnPowerBroadcast(DWORD dwPowerEvent, DWORD dwData)
+#define CR_MSG_WM_POWERBROADCAST(func) \
+ if (uMsg == WM_POWERBROADCAST) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((DWORD)wParam, (DWORD)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPrint(CDCHandle dc, UINT uFlags)
+#define CR_MSG_WM_PRINT(func) \
+ if (uMsg == WM_PRINT) { \
+ SetMsgHandled(TRUE); \
+ func((HDC)wParam, (UINT)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPrintClient(CDCHandle dc, UINT uFlags)
+#define CR_MSG_WM_PRINTCLIENT(func) \
+ if (uMsg == WM_PRINTCLIENT) { \
+ SetMsgHandled(TRUE); \
+ func((HDC)wParam, (UINT)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnRasDialEvent(RASCONNSTATE rasconnstate, DWORD dwError)
+#define CR_MSG_WM_RASDIALEVENT(func) \
+ if (uMsg == WM_RASDIALEVENT) { \
+ SetMsgHandled(TRUE); \
+ func((RASCONNSTATE)wParam, (DWORD)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSetFont(CFont font, BOOL bRedraw)
+#define CR_MSG_WM_SETFONT(func) \
+ if (uMsg == WM_SETFONT) { \
+ SetMsgHandled(TRUE); \
+ func((HFONT)wParam, (BOOL)LOWORD(lParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnSetHotKey(int nVirtKey, UINT uFlags)
+#define CR_MSG_WM_SETHOTKEY(func) \
+ if (uMsg == WM_SETHOTKEY) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((int)LOBYTE(LOWORD(wParam)), \
+ (UINT)HIBYTE(LOWORD(wParam))); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HICON OnSetIcon(UINT uType, HICON hIcon)
+#define CR_MSG_WM_SETICON(func) \
+ if (uMsg == WM_SETICON) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)wParam, (HICON)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnSetRedraw(BOOL bRedraw)
+#define CR_MSG_WM_SETREDRAW(func) \
+ if (uMsg == WM_SETREDRAW) { \
+ SetMsgHandled(TRUE); \
+ func((BOOL)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnSetText(LPCTSTR lpstrText)
+#define CR_MSG_WM_SETTEXT(func) \
+ if (uMsg == WM_SETTEXT) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((LPCTSTR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnUserChanged()
+#define CR_MSG_WM_USERCHANGED(func) \
+ if (uMsg == WM_USERCHANGED) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// New NT4 & NT5 messages
+
+#if (_WIN32_WINNT >= 0x0400)
+
+// void OnMouseHover(WPARAM wParam, CPoint ptPos)
+#define CR_MSG_WM_MOUSEHOVER(func) \
+ if (uMsg == WM_MOUSEHOVER) { \
+ SetMsgHandled(TRUE); \
+ func(wParam, \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMouseLeave()
+#define CR_MSG_WM_MOUSELEAVE(func) \
+ if (uMsg == WM_MOUSELEAVE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+#endif /* _WIN32_WINNT >= 0x0400 */
+
+#if (WINVER >= 0x0500)
+
+// void OnMenuRButtonUp(WPARAM wParam, CMenu menu)
+#define CR_MSG_WM_MENURBUTTONUP(func) \
+ if (uMsg == WM_MENURBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func(wParam, (HMENU)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnMenuDrag(WPARAM wParam, CMenu menu)
+#define CR_MSG_WM_MENUDRAG(func) \
+ if (uMsg == WM_MENUDRAG) { \
+ SetMsgHandled(TRUE); \
+ lResult = func(wParam, (HMENU)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnMenuGetObject(PMENUGETOBJECTINFO info)
+#define CR_MSG_WM_MENUGETOBJECT(func) \
+ if (uMsg == WM_MENUGETOBJECT) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((PMENUGETOBJECTINFO)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnUnInitMenuPopup(UINT nID, CMenu menu)
+#define CR_MSG_WM_UNINITMENUPOPUP(func) \
+ if (uMsg == WM_UNINITMENUPOPUP) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(lParam), (HMENU)wParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnMenuCommand(WPARAM nIndex, CMenu menu)
+#define CR_MSG_WM_MENUCOMMAND(func) \
+ if (uMsg == WM_MENUCOMMAND) { \
+ SetMsgHandled(TRUE); \
+ func(wParam, (HMENU)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+#endif /* WINVER >= 0x0500 */
+
+#if (_WIN32_WINNT >= 0x0500)
+
+// BOOL OnAppCommand(CWindow wndFocus, short cmd, WORD uDevice, int dwKeys)
+#define CR_MSG_WM_APPCOMMAND(func) \
+ if (uMsg == WM_APPCOMMAND) { \
+ SetMsgHandled(TRUE); \
+ lResult = \
+ (LRESULT)func((HWND)wParam, GET_APPCOMMAND_LPARAM(lParam), \
+ GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam)); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNCXButtonDown(int fwButton, short nHittest, CPoint ptPos)
+#define CR_MSG_WM_NCXBUTTONDOWN(func) \
+ if (uMsg == WM_NCXBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNCXButtonUp(int fwButton, short nHittest, CPoint ptPos)
+#define CR_MSG_WM_NCXBUTTONUP(func) \
+ if (uMsg == WM_NCXBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnNCXButtonDblClk(int fwButton, short nHittest, CPoint ptPos)
+#define CR_MSG_WM_NCXBUTTONDBLCLK(func) \
+ if (uMsg == WM_NCXBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnXButtonDown(int fwButton, int dwKeys, CPoint ptPos)
+#define CR_MSG_WM_XBUTTONDOWN(func) \
+ if (uMsg == WM_XBUTTONDOWN) { \
+ SetMsgHandled(TRUE); \
+ func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnXButtonUp(int fwButton, int dwKeys, CPoint ptPos)
+#define CR_MSG_WM_XBUTTONUP(func) \
+ if (uMsg == WM_XBUTTONUP) { \
+ SetMsgHandled(TRUE); \
+ func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnXButtonDblClk(int fwButton, int dwKeys, CPoint ptPos)
+#define CR_MSG_WM_XBUTTONDBLCLK(func) \
+ if (uMsg == WM_XBUTTONDBLCLK) { \
+ SetMsgHandled(TRUE); \
+ func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), \
+ gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnChangeUIState(WORD nAction, WORD nState)
+#define CR_MSG_WM_CHANGEUISTATE(func) \
+ if (uMsg == WM_CHANGEUISTATE) { \
+ SetMsgHandled(TRUE); \
+ func(LOWORD(wParam), HIWORD(wParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnUpdateUIState(WORD nAction, WORD nState)
+#define CR_MSG_WM_UPDATEUISTATE(func) \
+ if (uMsg == WM_UPDATEUISTATE) { \
+ SetMsgHandled(TRUE); \
+ func(LOWORD(wParam), HIWORD(wParam)); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnQueryUIState()
+#define CR_MSG_WM_QUERYUISTATE(func) \
+ if (uMsg == WM_QUERYUISTATE) { \
+ SetMsgHandled(TRUE); \
+ lResult = func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+#endif // (_WIN32_WINNT >= 0x0500)
+
+#if (_WIN32_WINNT >= 0x0501)
+
+// void OnInput(WPARAM RawInputCode, HRAWINPUT hRawInput)
+#define CR_MSG_WM_INPUT(func) \
+ if (uMsg == WM_INPUT) { \
+ SetMsgHandled(TRUE); \
+ func(GET_RAWINPUT_CODE_WPARAM(wParam), (HRAWINPUT)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnUniChar(TCHAR nChar, UINT nRepCnt, UINT nFlags)
+#define CR_MSG_WM_UNICHAR(func) \
+ if (uMsg == WM_UNICHAR) { \
+ SetMsgHandled(TRUE); \
+ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, \
+ (UINT)((lParam & 0xFFFF0000) >> 16)); \
+ if (!ref.get() || msg_handled_) { \
+ lResult = (wParam == UNICODE_NOCHAR) ? TRUE : FALSE; \
+ return TRUE; \
+ } \
+ }
+
+// OnThemeChanged()
+#define CR_MSG_WM_THEMECHANGED(func) \
+ if (uMsg == WM_THEMECHANGED) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+#endif /* _WIN32_WINNT >= 0x0501 */
+
+///////////////////////////////////////////////////////////////////////////////
+// ATL defined messages
+
+// BOOL OnForwardMsg(LPMSG Msg, DWORD nUserData)
+#define CR_MSG_WM_FORWARDMSG(func) \
+ if (uMsg == WM_FORWARDMSG) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((LPMSG)lParam, (DWORD)wParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Dialog specific messages
+
+// LRESULT OnDMGetDefID()
+#define MSG_DM_GETDEFID(func) \
+ if (uMsg == DM_GETDEFID) { \
+ SetMsgHandled(TRUE); \
+ lResult = func(); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDMSetDefID(UINT DefID)
+#define MSG_DM_SETDEFID(func) \
+ if (uMsg == DM_SETDEFID) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnDMReposition()
+#define MSG_DM_REPOSITION(func) \
+ if (uMsg == DM_REPOSITION) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Reflected messages
+
+// void OnReflectedCommand(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define MSG_OCM_COMMAND(func) \
+ if (uMsg == OCM_COMMAND) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedNotify(int idCtrl, LPNMHDR pnmh)
+#define MSG_OCM_NOTIFY(func) \
+ if (uMsg == OCM_NOTIFY) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((int)wParam, (LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedParentNotify(UINT message, UINT nChildID, LPARAM lParam)
+#define MSG_OCM_PARENTNOTIFY(func) \
+ if (uMsg == OCM_PARENTNOTIFY) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
+#define MSG_OCM_DRAWITEM(func) \
+ if (uMsg == OCM_DRAWITEM) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT
+// lpMeasureItemStruct)
+#define MSG_OCM_MEASUREITEM(func) \
+ if (uMsg == OCM_MEASUREITEM) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnReflectedCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT
+// lpCompareItemStruct)
+#define MSG_OCM_COMPAREITEM(func) \
+ if (uMsg == OCM_COMPAREITEM) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct)
+#define MSG_OCM_DELETEITEM(func) \
+ if (uMsg == OCM_DELETEITEM) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \
+ lResult = TRUE; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnReflectedVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox)
+#define MSG_OCM_VKEYTOITEM(func) \
+ if (uMsg == OCM_VKEYTOITEM) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), \
+ (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// int OnReflectedCharToItem(UINT nChar, UINT nIndex, CListBox listBox)
+#define MSG_OCM_CHARTOITEM(func) \
+ if (uMsg == OCM_CHARTOITEM) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), \
+ (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)
+#define MSG_OCM_HSCROLL(func) \
+ if (uMsg == OCM_HSCROLL) { \
+ SetMsgHandled(TRUE); \
+ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)
+#define MSG_OCM_VSCROLL(func) \
+ if (uMsg == OCM_VSCROLL) { \
+ SetMsgHandled(TRUE); \
+ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnReflectedCtlColorEdit(CDCHandle dc, CEdit edit)
+#define MSG_OCM_CTLCOLOREDIT(func) \
+ if (uMsg == OCM_CTLCOLOREDIT) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnReflectedCtlColorListBox(CDCHandle dc, CListBox listBox)
+#define MSG_OCM_CTLCOLORLISTBOX(func) \
+ if (uMsg == OCM_CTLCOLORLISTBOX) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnReflectedCtlColorBtn(CDCHandle dc, CButton button)
+#define MSG_OCM_CTLCOLORBTN(func) \
+ if (uMsg == OCM_CTLCOLORBTN) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnReflectedCtlColorDlg(CDCHandle dc, CWindow wnd)
+#define MSG_OCM_CTLCOLORDLG(func) \
+ if (uMsg == OCM_CTLCOLORDLG) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnReflectedCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar)
+#define MSG_OCM_CTLCOLORSCROLLBAR(func) \
+ if (uMsg == OCM_CTLCOLORSCROLLBAR) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// HBRUSH OnReflectedCtlColorStatic(CDCHandle dc, CStatic wndStatic)
+#define MSG_OCM_CTLCOLORSTATIC(func) \
+ if (uMsg == OCM_CTLCOLORSTATIC) { \
+ SetMsgHandled(TRUE); \
+ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Edit specific messages
+
+// void OnClear()
+#define CR_MSG_WM_CLEAR(func) \
+ if (uMsg == WM_CLEAR) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCopy()
+#define CR_MSG_WM_COPY(func) \
+ if (uMsg == WM_COPY) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCut()
+#define CR_MSG_WM_CUT(func) \
+ if (uMsg == WM_CUT) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnPaste()
+#define CR_MSG_WM_PASTE(func) \
+ if (uMsg == WM_PASTE) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnUndo()
+#define CR_MSG_WM_UNDO(func) \
+ if (uMsg == WM_UNDO) { \
+ SetMsgHandled(TRUE); \
+ func(); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Generic message handlers
+
+// LRESULT OnMessageHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam)
+#define CR_MESSAGE_HANDLER_EX(msg, func) \
+ if (uMsg == msg) { \
+ SetMsgHandled(TRUE); \
+ lResult = func(uMsg, wParam, lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnMessageRangeHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam)
+#define CR_MESSAGE_RANGE_HANDLER_EX(msgFirst, msgLast, func) \
+ if (uMsg >= msgFirst && uMsg <= msgLast) { \
+ SetMsgHandled(TRUE); \
+ lResult = func(uMsg, wParam, lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Commands and notifications
+
+// void OnCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define CR_COMMAND_HANDLER_EX(id, code, func) \
+ if (uMsg == WM_COMMAND && code == HIWORD(wParam) && id == LOWORD(wParam)) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define CR_COMMAND_ID_HANDLER_EX(id, func) \
+ if (uMsg == WM_COMMAND && id == LOWORD(wParam)) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define CR_COMMAND_CODE_HANDLER_EX(code, func) \
+ if (uMsg == WM_COMMAND && code == HIWORD(wParam)) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNotifyHandlerEX(LPNMHDR pnmh)
+#define CR_NOTIFY_HANDLER_EX(id, cd, func) \
+ if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \
+ id == ((LPNMHDR)lParam)->idFrom) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNotifyIDHandlerEX(LPNMHDR pnmh)
+#define CR_NOTIFY_ID_HANDLER_EX(id, func) \
+ if (uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNotifyCodeHandlerEX(LPNMHDR pnmh)
+#define CR_NOTIFY_CODE_HANDLER_EX(cd, func) \
+ if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define CR_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \
+ if (uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst && \
+ LOWORD(wParam) <= idLast) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
+#define CR_COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \
+ if (uMsg == WM_COMMAND && code == HIWORD(wParam) && \
+ LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNotifyRangeHandlerEX(LPNMHDR pnmh)
+#define CR_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \
+ if (uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && \
+ ((LPNMHDR)lParam)->idFrom <= idLast) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnNotifyRangeCodeHandlerEX(LPNMHDR pnmh)
+#define CR_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \
+ if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \
+ ((LPNMHDR)lParam)->idFrom >= idFirst && \
+ ((LPNMHDR)lParam)->idFrom <= idLast) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedCommandHandlerEX(UINT uNotifyCode, int nID, CWindow
+// wndCtl)
+#define CR_REFLECTED_COMMAND_HANDLER_EX(id, code, func) \
+ if (uMsg == OCM_COMMAND && code == HIWORD(wParam) && id == LOWORD(wParam)) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow
+// wndCtl)
+#define CR_REFLECTED_COMMAND_ID_HANDLER_EX(id, func) \
+ if (uMsg == OCM_COMMAND && id == LOWORD(wParam)) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow
+// wndCtl)
+#define CR_REFLECTED_COMMAND_CODE_HANDLER_EX(code, func) \
+ if (uMsg == OCM_COMMAND && code == HIWORD(wParam)) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedNotifyHandlerEX(LPNMHDR pnmh)
+#define CR_REFLECTED_NOTIFY_HANDLER_EX(id, cd, func) \
+ if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \
+ id == ((LPNMHDR)lParam)->idFrom) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedNotifyIDHandlerEX(LPNMHDR pnmh)
+#define CR_REFLECTED_NOTIFY_ID_HANDLER_EX(id, func) \
+ if (uMsg == OCM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedNotifyCodeHandlerEX(LPNMHDR pnmh)
+#define CR_REFLECTED_NOTIFY_CODE_HANDLER_EX(cd, func) \
+ if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow
+// wndCtl)
+#define CR_REFLECTED_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \
+ if (uMsg == OCM_COMMAND && LOWORD(wParam) >= idFirst && \
+ LOWORD(wParam) <= idLast) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// void OnReflectedCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow
+// wndCtl)
+#define CR_REFLECTED_COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, \
+ func) \
+ if (uMsg == OCM_COMMAND && code == HIWORD(wParam) && \
+ LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) { \
+ SetMsgHandled(TRUE); \
+ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \
+ lResult = 0; \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedNotifyRangeHandlerEX(LPNMHDR pnmh)
+#define CR_REFLECTED_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \
+ if (uMsg == OCM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && \
+ ((LPNMHDR)lParam)->idFrom <= idLast) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+// LRESULT OnReflectedNotifyRangeCodeHandlerEX(LPNMHDR pnmh)
+#define CR_REFLECTED_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \
+ if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \
+ ((LPNMHDR)lParam)->idFrom >= idFirst && \
+ ((LPNMHDR)lParam)->idFrom <= idLast) { \
+ SetMsgHandled(TRUE); \
+ lResult = func((LPNMHDR)lParam); \
+ if (!ref.get() || msg_handled_) \
+ return TRUE; \
+ }
+
+#define CR_DEFLATE_RECT(rect, by) \
+ { \
+ (rect)->left += (by)->left; \
+ (rect)->top += (by)->top; \
+ (rect)->right -= (by)->right; \
+ (rect)->bottom -= (by)->bottom; \
+ }
+
+#define CR_POINT_INITIALIZER_FROM_LPARAM(lparam) \
+ { LOWORD(lparam), HIWORD(lparam) }
+
+#endif // UI_GFX_WIN_MSG_UTIL_H_
diff --git a/ui/gfx/win/physical_size.cc b/ui/gfx/win/physical_size.cc
new file mode 100644
index 0000000..39978f7
--- /dev/null
+++ b/ui/gfx/win/physical_size.cc
@@ -0,0 +1,163 @@
+// Copyright 2016 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 "ui/gfx/win/physical_size.h"
+
+#include <windows.h>
+#include <setupapi.h>
+
+#include <iostream>
+#include <memory>
+
+#include "base/check_op.h"
+#include "base/memory/free_deleter.h"
+#include "base/scoped_generic.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+
+// This GUID {E6F07B5F-EE97-4A90-B076-33F57BF4EAA7} was taken from
+// https://msdn.microsoft.com/en-us/library/windows/hardware/ff545901.aspx
+const GUID GUID_DEVICEINTERFACE_MONITOR = {
+ 0xE6F07B5F,
+ 0xEE97,
+ 0x4A90,
+ {0xB0, 0x76, 0x33, 0xF5, 0x7B, 0xF4, 0xEA, 0xA7}};
+
+namespace {
+
+struct DeviceInfoListScopedTraits {
+ static HDEVINFO InvalidValue() { return INVALID_HANDLE_VALUE; }
+
+ static void Free(HDEVINFO h) { SetupDiDestroyDeviceInfoList(h); }
+};
+
+bool GetSizeFromRegistry(HDEVINFO device_info_list,
+ SP_DEVINFO_DATA* device_info,
+ int* width_mm,
+ int* height_mm) {
+ base::win::RegKey reg_key(SetupDiOpenDevRegKey(
+ device_info_list, device_info, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ));
+ if (!reg_key.Valid())
+ return false;
+
+ BYTE data[128]; // EDID block is exactly 128 bytes long.
+ ZeroMemory(&data[0], sizeof(data));
+ DWORD data_length = sizeof(data);
+ LONG return_value =
+ reg_key.ReadValue(L"EDID", &data[0], &data_length, nullptr);
+ if (return_value != ERROR_SUCCESS)
+ return false;
+
+ // Byte 54 is the start of the first descriptor block, which contains the
+ // required timing information with the highest preference, and 12 bytes
+ // into that block is the size information.
+ // 66: width least significant bits
+ // 67: height least significant bits
+ // 68: 4 bits for each of width and height most significant bits
+ if (data[54] == 0)
+ return false;
+ const int w = ((data[68] & 0xF0) << 4) + data[66];
+ const int h = ((data[68] & 0x0F) << 8) + data[67];
+
+ if (w <= 0 || h <= 0)
+ return false;
+
+ *width_mm = w;
+ *height_mm = h;
+
+ return true;
+}
+
+bool GetInterfaceDetailAndDeviceInfo(
+ HDEVINFO device_info_list,
+ SP_DEVICE_INTERFACE_DATA* interface_data,
+ std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>*
+ interface_detail,
+ SP_DEVINFO_DATA* device_info) {
+ DCHECK_EQ(sizeof(*device_info), device_info->cbSize);
+ DWORD buffer_size;
+ // This call populates device_info. It will also fail, but if the error is
+ // "insufficient buffer" then it will set buffer_size and we can call again
+ // with an allocated buffer.
+ SetupDiGetDeviceInterfaceDetail(device_info_list, interface_data, nullptr, 0,
+ &buffer_size, device_info);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return false;
+
+ interface_detail->reset(
+ reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(buffer_size)));
+ (*interface_detail)->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+ return SetupDiGetDeviceInterfaceDetail(device_info_list, interface_data,
+ interface_detail->get(), buffer_size,
+ nullptr, nullptr) != 0;
+}
+
+} // namespace
+
+namespace gfx {
+
+// The physical size information is only available by looking in the EDID block
+// via setup. However setup has the device path and not the device name that we
+// use to identify displays. Therefore after looking up a device via setup we
+// need to find the display again via EnumDisplayDevices (matching device path
+// to the device ID of the display's interface) so we can return the device name
+// (available from the interface's attached monitor).
+std::vector<PhysicalDisplaySize> GetPhysicalSizeForDisplays() {
+ std::vector<PhysicalDisplaySize> out;
+
+ base::ScopedGeneric<HDEVINFO, DeviceInfoListScopedTraits> device_info_list(
+ SetupDiGetClassDevs(&GUID_DEVICEINTERFACE_MONITOR, nullptr, nullptr,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
+
+ if (!device_info_list.is_valid())
+ return out;
+
+ SP_DEVICE_INTERFACE_DATA interface_data = {};
+ interface_data.cbSize = sizeof(interface_data);
+ int interface_index = 0;
+ while (SetupDiEnumDeviceInterfaces(device_info_list.get(), nullptr,
+ &GUID_DEVICEINTERFACE_MONITOR,
+ interface_index++, &interface_data)) {
+ std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
+ interface_detail;
+ SP_DEVINFO_DATA device_info = {};
+ device_info.cbSize = sizeof(device_info);
+ bool get_info_succeeded =
+ GetInterfaceDetailAndDeviceInfo(device_info_list.get(), &interface_data,
+ &interface_detail, &device_info);
+ if (!get_info_succeeded)
+ continue;
+
+ DISPLAY_DEVICE display_device = {};
+ display_device.cb = sizeof(display_device);
+ int display_index = 0;
+ while (EnumDisplayDevices(nullptr, display_index++, &display_device,
+ EDD_GET_DEVICE_INTERFACE_NAME)) {
+ DISPLAY_DEVICE attached_device = {};
+ attached_device.cb = sizeof(attached_device);
+ int attached_index = 0;
+ while (EnumDisplayDevices(display_device.DeviceName, attached_index++,
+ &attached_device,
+ EDD_GET_DEVICE_INTERFACE_NAME)) {
+ wchar_t* attached_device_id = attached_device.DeviceID;
+ wchar_t* setup_device_path = interface_detail->DevicePath;
+ if (wcsicmp(attached_device_id, setup_device_path) == 0) {
+ int width_mm;
+ int height_mm;
+ bool found = GetSizeFromRegistry(device_info_list.get(), &device_info,
+ &width_mm, &height_mm);
+ if (found) {
+ out.push_back(
+ PhysicalDisplaySize(base::WideToUTF8(display_device.DeviceName),
+ width_mm, height_mm));
+ }
+ break;
+ }
+ }
+ }
+ }
+ return out;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/physical_size.h b/ui/gfx/win/physical_size.h
new file mode 100644
index 0000000..5b768ad
--- /dev/null
+++ b/ui/gfx/win/physical_size.h
@@ -0,0 +1,31 @@
+// Copyright 2016 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 UI_GFX_WIN_PHYSICAL_SIZE_H_
+#define UI_GFX_WIN_PHYSICAL_SIZE_H_
+
+#include <string>
+#include <vector>
+
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+struct PhysicalDisplaySize {
+ PhysicalDisplaySize(const std::string& display_name,
+ int width_mm,
+ int height_mm)
+ : display_name(display_name), width_mm(width_mm), height_mm(height_mm) {}
+
+ std::string display_name;
+ int width_mm;
+ int height_mm;
+};
+
+// Gets the physical size for all displays.
+GFX_EXPORT std::vector<PhysicalDisplaySize> GetPhysicalSizeForDisplays();
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_PHYSICAL_SIZE_H_
diff --git a/ui/gfx/win/rendering_window_manager.cc b/ui/gfx/win/rendering_window_manager.cc
new file mode 100644
index 0000000..8536882
--- /dev/null
+++ b/ui/gfx/win/rendering_window_manager.cc
@@ -0,0 +1,80 @@
+// Copyright 2016 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 "ui/gfx/win/rendering_window_manager.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace gfx {
+
+// static
+RenderingWindowManager* RenderingWindowManager::GetInstance() {
+ static base::NoDestructor<RenderingWindowManager> instance;
+ return instance.get();
+}
+
+void RenderingWindowManager::RegisterParent(HWND parent) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ registered_hwnds_.emplace(parent, nullptr);
+}
+
+void RenderingWindowManager::RegisterChild(HWND parent,
+ HWND child,
+ DWORD expected_child_process_id) {
+ if (!child)
+ return;
+
+ // This can be called from any thread, if we're not on the correct thread then
+ // PostTask back to the UI thread before doing anything.
+ if (!task_runner_->BelongsToCurrentThread()) {
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&RenderingWindowManager::RegisterChild,
+ base::Unretained(this), parent, child,
+ expected_child_process_id));
+ return;
+ }
+
+ // Check that |parent| was registered as a HWND that could have a child HWND.
+ auto it = registered_hwnds_.find(parent);
+ if (it == registered_hwnds_.end())
+ return;
+
+ // Check that |child| belongs to the GPU process.
+ DWORD child_process_id = 0;
+ DWORD child_thread_id = GetWindowThreadProcessId(child, &child_process_id);
+ if (!child_thread_id || child_process_id != expected_child_process_id) {
+ DLOG(ERROR) << "Child HWND not owned by GPU process.";
+ return;
+ }
+
+ it->second = child;
+
+ ::SetParent(child, parent);
+ // Move D3D window behind Chrome's window to avoid losing some messages.
+ ::SetWindowPos(child, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+void RenderingWindowManager::UnregisterParent(HWND parent) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ registered_hwnds_.erase(parent);
+}
+
+bool RenderingWindowManager::HasValidChildWindow(HWND parent) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ auto it = registered_hwnds_.find(parent);
+ if (it == registered_hwnds_.end())
+ return false;
+ return !!it->second && ::IsWindow(it->second);
+}
+
+RenderingWindowManager::RenderingWindowManager()
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+
+RenderingWindowManager::~RenderingWindowManager() = default;
+
+} // namespace gfx
diff --git a/ui/gfx/win/rendering_window_manager.h b/ui/gfx/win/rendering_window_manager.h
new file mode 100644
index 0000000..dd729b1
--- /dev/null
+++ b/ui/gfx/win/rendering_window_manager.h
@@ -0,0 +1,59 @@
+// Copyright 2016 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 UI_GFX_WIN_RENDERING_WINDOW_MANAGER_H_
+#define UI_GFX_WIN_RENDERING_WINDOW_MANAGER_H_
+
+#include <windows.h>
+
+#include "base/containers/flat_map.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace gfx {
+
+// This keeps track of whether a given HWND has a child window which the GPU
+// process renders into. This should only be used from the UI thread unless
+// otherwise noted.
+class GFX_EXPORT RenderingWindowManager {
+ public:
+ // The first call to GetInstance() should happen on the UI thread.
+ static RenderingWindowManager* GetInstance();
+
+ RenderingWindowManager(const RenderingWindowManager&) = delete;
+ RenderingWindowManager& operator=(const RenderingWindowManager&) = delete;
+
+ void RegisterParent(HWND parent);
+ // Registers |child| as child window for |parent|. Allows the GPU process to
+ // draw into the |child| HWND instead of |parent|. This will fail and do
+ // nothing if:
+ // 1. |parent| isn't registered.
+ // 2. |child| doesn't belong to |expected_child_process_id|.
+ //
+ // Can be called from any thread, as long GetInstance() has already been
+ // called on the UI thread at least once.
+ void RegisterChild(HWND parent, HWND child, DWORD expected_child_process_id);
+ void UnregisterParent(HWND parent);
+ bool HasValidChildWindow(HWND parent);
+
+ private:
+ friend class base::NoDestructor<RenderingWindowManager>;
+
+ RenderingWindowManager();
+ ~RenderingWindowManager();
+
+ // UI thread task runner.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ // Map from registered parent HWND to child HWND.
+ base::flat_map<HWND, HWND> registered_hwnds_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_RENDERING_WINDOW_MANAGER_H_
diff --git a/ui/gfx/win/scoped_set_map_mode.h b/ui/gfx/win/scoped_set_map_mode.h
new file mode 100644
index 0000000..f5669fe
--- /dev/null
+++ b/ui/gfx/win/scoped_set_map_mode.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_WIN_SCOPED_SET_MAP_MODE_H_
+#define UI_GFX_WIN_SCOPED_SET_MAP_MODE_H_
+
+#include <windows.h>
+
+#include "base/check_op.h"
+#include "base/macros.h"
+
+namespace gfx {
+
+// Helper class for setting and restore the map mode on a DC.
+class ScopedSetMapMode {
+ public:
+ ScopedSetMapMode(HDC hdc, int map_mode)
+ : hdc_(hdc),
+ old_map_mode_(SetMapMode(hdc, map_mode)) {
+ DCHECK(hdc_);
+ DCHECK_NE(map_mode, 0);
+ DCHECK_NE(old_map_mode_, 0);
+ }
+
+ ScopedSetMapMode(const ScopedSetMapMode&) = delete;
+ ScopedSetMapMode& operator=(const ScopedSetMapMode&) = delete;
+
+ ~ScopedSetMapMode() {
+ const int mode = SetMapMode(hdc_, old_map_mode_);
+ DCHECK_NE(mode, 0);
+ }
+
+ private:
+ HDC hdc_;
+ int old_map_mode_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_SCOPED_SET_MAP_MODE_H_
diff --git a/ui/gfx/win/singleton_hwnd.cc b/ui/gfx/win/singleton_hwnd.cc
new file mode 100644
index 0000000..1476b11
--- /dev/null
+++ b/ui/gfx/win/singleton_hwnd.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/win/singleton_hwnd.h"
+
+#include "base/memory/singleton.h"
+#include "base/task/current_thread.h"
+#include "ui/gfx/win/singleton_hwnd_observer.h"
+
+namespace gfx {
+
+// static
+SingletonHwnd* SingletonHwnd::GetInstance() {
+ return base::Singleton<SingletonHwnd>::get();
+}
+
+BOOL SingletonHwnd::ProcessWindowMessage(HWND window,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT& result,
+ DWORD msg_map_id) {
+ if (!base::CurrentUIThread::IsSet()) {
+ // If there is no MessageLoop and SingletonHwnd is receiving messages, this
+ // means it is receiving messages via an external message pump such as COM
+ // uninitialization.
+ //
+ // It is unsafe to forward these messages as observers may depend on the
+ // existence of a MessageLoop to proceed.
+ return false;
+ }
+
+ for (SingletonHwndObserver& observer : observer_list_)
+ observer.OnWndProc(window, message, wparam, lparam);
+ return false;
+}
+
+SingletonHwnd::SingletonHwnd() {
+ if (!base::CurrentUIThread::IsSet()) {
+ // Creating this window in (e.g.) a renderer inhibits shutdown on
+ // Windows. See http://crbug.com/230122 and http://crbug.com/236039.
+ return;
+ }
+ WindowImpl::Init(NULL, Rect());
+}
+
+SingletonHwnd::~SingletonHwnd() {
+ // WindowImpl will clean up the hwnd value on WM_NCDESTROY.
+ if (hwnd())
+ DestroyWindow(hwnd());
+
+ // Tell all of our current observers to clean themselves up.
+ for (SingletonHwndObserver& observer : observer_list_)
+ observer.ClearWndProc();
+}
+
+void SingletonHwnd::AddObserver(SingletonHwndObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void SingletonHwnd::RemoveObserver(SingletonHwndObserver* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/singleton_hwnd.h b/ui/gfx/win/singleton_hwnd.h
new file mode 100644
index 0000000..36daf55
--- /dev/null
+++ b/ui/gfx/win/singleton_hwnd.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_WIN_SINGLETON_HWND_H_
+#define UI_GFX_WIN_SINGLETON_HWND_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/win/window_impl.h"
+
+namespace base {
+template<typename T> struct DefaultSingletonTraits;
+}
+
+namespace gfx {
+
+class SingletonHwndObserver;
+
+// Singleton message-only HWND that allows interested clients to receive WM_*
+// notifications.
+class GFX_EXPORT SingletonHwnd : public WindowImpl {
+ public:
+ static SingletonHwnd* GetInstance();
+
+ SingletonHwnd(const SingletonHwnd&) = delete;
+ SingletonHwnd& operator=(const SingletonHwnd&) = delete;
+
+ // Windows callback for WM_* notifications.
+ BOOL ProcessWindowMessage(HWND window,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT& result,
+ DWORD msg_map_id) override;
+
+ private:
+ friend class SingletonHwndObserver;
+ friend struct base::DefaultSingletonTraits<SingletonHwnd>;
+
+ SingletonHwnd();
+ ~SingletonHwnd() override;
+
+ // Add/remove SingletonHwndObserver to forward WM_* notifications.
+ void AddObserver(SingletonHwndObserver* observer);
+ void RemoveObserver(SingletonHwndObserver* observer);
+
+ // List of registered observers.
+ base::ObserverList<SingletonHwndObserver, true>::Unchecked observer_list_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_SINGLETON_HWND_H_
diff --git a/ui/gfx/win/singleton_hwnd_hot_key_observer.cc b/ui/gfx/win/singleton_hwnd_hot_key_observer.cc
new file mode 100644
index 0000000..be3d7c2
--- /dev/null
+++ b/ui/gfx/win/singleton_hwnd_hot_key_observer.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 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 "ui/gfx/win/singleton_hwnd_hot_key_observer.h"
+
+#include "base/bind.h"
+#include "base/containers/flat_set.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/win/singleton_hwnd.h"
+
+namespace gfx {
+
+namespace {
+
+base::flat_set<int>& GetUsedHotKeyIDs() {
+ static base::NoDestructor<base::flat_set<int>> used_hot_key_ids;
+ return *used_hot_key_ids;
+}
+
+absl::optional<int> GetAvailableHotKeyID() {
+ // Valid hot key IDs are in the range 0x0000 to 0xBFFF. See
+ // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registerhotkey
+ for (int i = 0x0000; i < 0xBFFF; i++) {
+ if (!GetUsedHotKeyIDs().contains(i))
+ return i;
+ }
+ return absl::nullopt;
+}
+
+void SetHotKeyIDUsed(int id) {
+ DCHECK(!GetUsedHotKeyIDs().contains(id));
+ GetUsedHotKeyIDs().insert(id);
+}
+
+void SetHotKeyIDAvailable(int id) {
+ DCHECK(GetUsedHotKeyIDs().contains(id));
+ GetUsedHotKeyIDs().erase(id);
+}
+
+} // anonymous namespace
+
+std::unique_ptr<SingletonHwndHotKeyObserver>
+SingletonHwndHotKeyObserver::Create(
+ const SingletonHwndObserver::WndProc& wnd_proc,
+ UINT key_code,
+ int modifiers) {
+ absl::optional<int> hot_key_id = GetAvailableHotKeyID();
+
+ // If there are no available hot key IDs, return null.
+ if (!hot_key_id.has_value())
+ return nullptr;
+
+ // If we fail to register the hot key, return null. Most likely reason for
+ // failure is that another application has already registered the hot key.
+ if (!RegisterHotKey(gfx::SingletonHwnd::GetInstance()->hwnd(), *hot_key_id,
+ modifiers, key_code)) {
+ return nullptr;
+ }
+
+ return base::WrapUnique(
+ new SingletonHwndHotKeyObserver(wnd_proc, *hot_key_id));
+}
+
+SingletonHwndHotKeyObserver::SingletonHwndHotKeyObserver(
+ const SingletonHwndObserver::WndProc& wnd_proc,
+ int hot_key_id)
+ : observer_(base::BindRepeating(&SingletonHwndHotKeyObserver::OnWndProc,
+ base::Unretained(this))),
+ wnd_proc_(wnd_proc),
+ hot_key_id_(hot_key_id) {
+ SetHotKeyIDUsed(hot_key_id);
+}
+
+SingletonHwndHotKeyObserver::~SingletonHwndHotKeyObserver() {
+ bool success = !!UnregisterHotKey(gfx::SingletonHwnd::GetInstance()->hwnd(),
+ hot_key_id_);
+ // This call should always succeed, as long as we pass in the right HWND and
+ // an id we've used to register before.
+ DCHECK(success);
+ SetHotKeyIDAvailable(hot_key_id_);
+}
+
+void SingletonHwndHotKeyObserver::OnWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ // Only propagate WM_HOTKEY messages for this particular hot key to the owner
+ // of this observer.
+ if (message == WM_HOTKEY && static_cast<int>(wparam) == hot_key_id_)
+ wnd_proc_.Run(hwnd, message, wparam, lparam);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/singleton_hwnd_hot_key_observer.h b/ui/gfx/win/singleton_hwnd_hot_key_observer.h
new file mode 100644
index 0000000..5b003ce
--- /dev/null
+++ b/ui/gfx/win/singleton_hwnd_hot_key_observer.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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 UI_GFX_WIN_SINGLETON_HWND_HOT_KEY_OBSERVER_H_
+#define UI_GFX_WIN_SINGLETON_HWND_HOT_KEY_OBSERVER_H_
+
+#include "base/win/windows_types.h"
+#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/win/singleton_hwnd_observer.h"
+
+namespace gfx {
+
+// We need to avoid duplicate hot key IDs for SingletonHwndObservers that call
+// RegisterHotKey for SingletonHwnd. This class properly handles getting a
+// unique hot key ID, registers the hotkey on construction, and unregisters the
+// hot key on destruction.
+//
+// This class should always be used instead of directly registering hot keys on
+// the SingletonHwnd with a SingletonHwndObserver in order to prevent duplicate
+// hot key IDs.
+class GFX_EXPORT SingletonHwndHotKeyObserver {
+ public:
+ // Registers a hot key with the given |key_code| and |modifiers| and returns
+ // a SingletonHwndHotKeyObserver if successful. Returns null if the hot key
+ // fails to register, which can happen if another application has already
+ // registered the hot key.
+ static std::unique_ptr<SingletonHwndHotKeyObserver> Create(
+ const SingletonHwndObserver::WndProc& wnd_proc,
+ UINT key_code,
+ int modifiers);
+
+ SingletonHwndHotKeyObserver(const SingletonHwndHotKeyObserver&) = delete;
+ SingletonHwndHotKeyObserver& operator=(const SingletonHwndHotKeyObserver&) =
+ delete;
+
+ ~SingletonHwndHotKeyObserver();
+
+ private:
+ SingletonHwndHotKeyObserver(const SingletonHwndObserver::WndProc& wnd_proc,
+ int hot_key_id);
+
+ // Called by SingletonHwndObserver.
+ void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+ SingletonHwndObserver observer_;
+ SingletonHwndObserver::WndProc wnd_proc_;
+ const int hot_key_id_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_SINGLETON_HWND_HOT_KEY_OBSERVER_H_
diff --git a/ui/gfx/win/singleton_hwnd_observer.cc b/ui/gfx/win/singleton_hwnd_observer.cc
new file mode 100644
index 0000000..f439105
--- /dev/null
+++ b/ui/gfx/win/singleton_hwnd_observer.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 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 "ui/gfx/win/singleton_hwnd_observer.h"
+
+#include "ui/gfx/win/singleton_hwnd.h"
+
+namespace gfx {
+
+SingletonHwndObserver::SingletonHwndObserver(const WndProc& wnd_proc)
+ : wnd_proc_(wnd_proc) {
+ DCHECK(!wnd_proc.is_null());
+ SingletonHwnd::GetInstance()->AddObserver(this);
+}
+
+SingletonHwndObserver::~SingletonHwndObserver() {
+ ClearWndProc();
+}
+
+void SingletonHwndObserver::ClearWndProc() {
+ if (!wnd_proc_.is_null()) {
+ SingletonHwnd::GetInstance()->RemoveObserver(this);
+ wnd_proc_.Reset();
+ }
+}
+
+void SingletonHwndObserver::OnWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ wnd_proc_.Run(hwnd, message, wparam, lparam);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/singleton_hwnd_observer.h b/ui/gfx/win/singleton_hwnd_observer.h
new file mode 100644
index 0000000..cc2ef8a
--- /dev/null
+++ b/ui/gfx/win/singleton_hwnd_observer.h
@@ -0,0 +1,44 @@
+// Copyright 2015 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 UI_GFX_WIN_SINGLETON_HWND_OBSERVER_H_
+#define UI_GFX_WIN_SINGLETON_HWND_OBSERVER_H_
+
+#include <windows.h>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+class SingletonHwnd;
+
+// Singleton lifetime management is tricky. This observer handles the correct
+// cleanup if either the SingletonHwnd or forwarded object is destroyed first.
+// Note that if you want to register a hot key on the SingletonHwnd, you need to
+// use a SingletonHwndHotKeyObserver instead for each hot key.
+class GFX_EXPORT SingletonHwndObserver {
+ public:
+ using WndProc = base::RepeatingCallback<void(HWND, UINT, WPARAM, LPARAM)>;
+
+ explicit SingletonHwndObserver(const WndProc& wnd_proc);
+
+ SingletonHwndObserver(const SingletonHwndObserver&) = delete;
+ SingletonHwndObserver& operator=(const SingletonHwndObserver&) = delete;
+
+ ~SingletonHwndObserver();
+
+ private:
+ friend class SingletonHwnd;
+
+ void ClearWndProc();
+ void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+ WndProc wnd_proc_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_SINGLETON_HWND_OBSERVER_H_
diff --git a/ui/gfx/win/text_analysis_source.cc b/ui/gfx/win/text_analysis_source.cc
new file mode 100644
index 0000000..13fef96
--- /dev/null
+++ b/ui/gfx/win/text_analysis_source.cc
@@ -0,0 +1,95 @@
+// Copyright 2016 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 "ui/gfx/win/text_analysis_source.h"
+
+#include "base/check.h"
+
+namespace gfx {
+namespace win {
+
+HRESULT TextAnalysisSource::Create(
+ IDWriteTextAnalysisSource** text_analysis_out,
+ const std::wstring& text,
+ const std::wstring& locale_name,
+ IDWriteNumberSubstitution* number_substitution,
+ DWRITE_READING_DIRECTION reading_direction) {
+ return Microsoft::WRL::MakeAndInitialize<gfx::win::TextAnalysisSource>(
+ text_analysis_out, text, locale_name, number_substitution,
+ reading_direction);
+}
+
+TextAnalysisSource::TextAnalysisSource() = default;
+TextAnalysisSource::~TextAnalysisSource() = default;
+
+HRESULT TextAnalysisSource::GetLocaleName(UINT32 text_position,
+ UINT32* text_length,
+ const WCHAR** locale_name) {
+ if (text_position >= text_.length() || !text_length || !locale_name)
+ return E_INVALIDARG;
+ *text_length = text_.length() - text_position;
+ *locale_name = locale_name_.c_str();
+ return S_OK;
+}
+
+HRESULT TextAnalysisSource::GetNumberSubstitution(
+ UINT32 text_position,
+ UINT32* text_length,
+ IDWriteNumberSubstitution** number_substitution) {
+ if (text_position >= text_.length() || !text_length || !number_substitution)
+ return E_INVALIDARG;
+ *text_length = text_.length() - text_position;
+ number_substitution_.CopyTo(number_substitution);
+ return S_OK;
+}
+
+DWRITE_READING_DIRECTION TextAnalysisSource::GetParagraphReadingDirection() {
+ return reading_direction_;
+}
+
+HRESULT TextAnalysisSource::GetTextAtPosition(UINT32 text_position,
+ const WCHAR** text_string,
+ UINT32* text_length) {
+ if (!text_length || !text_string)
+ return E_INVALIDARG;
+ if (text_position >= text_.length()) {
+ *text_string = nullptr;
+ *text_length = 0;
+ return S_OK;
+ }
+ *text_string = text_.c_str() + text_position;
+ *text_length = text_.length() - text_position;
+ return S_OK;
+}
+
+HRESULT TextAnalysisSource::GetTextBeforePosition(UINT32 text_position,
+ const WCHAR** text_string,
+ UINT32* text_length) {
+ if (!text_length || !text_string)
+ return E_INVALIDARG;
+ if (text_position < 1 || text_position > text_.length()) {
+ *text_string = nullptr;
+ *text_length = 0;
+ return S_OK;
+ }
+ *text_string = text_.c_str();
+ *text_length = text_position;
+ return S_OK;
+}
+
+HRESULT TextAnalysisSource::RuntimeClassInitialize(
+ const std::wstring& text,
+ const std::wstring& locale_name,
+ IDWriteNumberSubstitution* number_substitution,
+ DWRITE_READING_DIRECTION reading_direction) {
+ DCHECK(number_substitution);
+ text_ = text;
+ locale_name_ = locale_name;
+ number_substitution_ = number_substitution;
+ reading_direction_ = reading_direction;
+ return S_OK;
+}
+
+} // namespace win
+} // namespace gfx
diff --git a/ui/gfx/win/text_analysis_source.h b/ui/gfx/win/text_analysis_source.h
new file mode 100644
index 0000000..93ce417
--- /dev/null
+++ b/ui/gfx/win/text_analysis_source.h
@@ -0,0 +1,78 @@
+// Copyright 2016 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 UI_GFX_WIN_TEXT_ANALYSIS_SOURCE_H_
+#define UI_GFX_WIN_TEXT_ANALYSIS_SOURCE_H_
+
+#include <dwrite.h>
+#include <wrl.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+namespace win {
+
+// Implements an IDWriteTextAnalysisSource, describing a single pre-defined
+// chunk of text with a uniform locale, reading direction, and number
+// substitution.
+class TextAnalysisSource
+ : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+ IDWriteTextAnalysisSource> {
+ public:
+ // Factory method to avoid exporting the class and all it derives from.
+ static GFX_EXPORT HRESULT
+ Create(IDWriteTextAnalysisSource** text_analysis_out,
+ const std::wstring& text,
+ const std::wstring& locale_name,
+ IDWriteNumberSubstitution* number_substitution,
+ DWRITE_READING_DIRECTION reading_direction);
+
+ // Use Create() to construct these objects. Direct calls to the constructor
+ // are an error - it is only public because a WRL helper function creates the
+ // objects.
+ TextAnalysisSource();
+
+ TextAnalysisSource& operator=(const TextAnalysisSource&) = delete;
+
+ // IDWriteTextAnalysisSource:
+ HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 text_position,
+ UINT32* text_length,
+ const WCHAR** locale_name) override;
+ HRESULT STDMETHODCALLTYPE GetNumberSubstitution(
+ UINT32 text_position,
+ UINT32* text_length,
+ IDWriteNumberSubstitution** number_substitution) override;
+ DWRITE_READING_DIRECTION STDMETHODCALLTYPE
+ GetParagraphReadingDirection() override;
+ HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 text_position,
+ const WCHAR** text_string,
+ UINT32* text_length) override;
+ HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 text_position,
+ const WCHAR** text_string,
+ UINT32* text_length) override;
+
+ HRESULT STDMETHODCALLTYPE
+ RuntimeClassInitialize(const std::wstring& text,
+ const std::wstring& locale_name,
+ IDWriteNumberSubstitution* number_substitution,
+ DWRITE_READING_DIRECTION reading_direction);
+
+ protected:
+ ~TextAnalysisSource() override;
+
+ private:
+ std::wstring text_;
+ std::wstring locale_name_;
+ Microsoft::WRL::ComPtr<IDWriteNumberSubstitution> number_substitution_;
+ DWRITE_READING_DIRECTION reading_direction_;
+};
+
+} // namespace win
+} // namespace gfx
+
+#endif // UI_GFX_WIN_TEXT_ANALYSIS_SOURCE_H_
diff --git a/ui/gfx/win/text_analysis_source_unittest.cc b/ui/gfx/win/text_analysis_source_unittest.cc
new file mode 100644
index 0000000..7886218
--- /dev/null
+++ b/ui/gfx/win/text_analysis_source_unittest.cc
@@ -0,0 +1,133 @@
+// Copyright 2016 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 "ui/gfx/win/text_analysis_source.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/win/direct_write.h"
+
+namespace mswr = Microsoft::WRL;
+
+namespace gfx {
+namespace win {
+
+namespace {
+
+DWRITE_READING_DIRECTION kReadingDirection =
+ DWRITE_READING_DIRECTION_TOP_TO_BOTTOM;
+const wchar_t* kLocale = L"hi-in";
+const wchar_t kText[] = L"sample text";
+const size_t kTextLength = _countof(kText) - 1;
+
+} // namespace
+
+class TextAnalysisSourceTest : public testing::Test {
+ public:
+ TextAnalysisSourceTest() {
+ mswr::ComPtr<IDWriteFactory> factory;
+ CreateDWriteFactory(&factory);
+
+ mswr::ComPtr<IDWriteNumberSubstitution> number_substitution;
+ factory->CreateNumberSubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE,
+ kLocale, true /* ignoreUserOverride */,
+ &number_substitution);
+
+ TextAnalysisSource::Create(&source_, kText, kLocale,
+ number_substitution.Get(), kReadingDirection);
+ }
+
+ TextAnalysisSourceTest(const TextAnalysisSourceTest&) = delete;
+ TextAnalysisSourceTest& operator=(const TextAnalysisSourceTest&) = delete;
+
+ protected:
+ mswr::ComPtr<IDWriteTextAnalysisSource> source_;
+};
+
+TEST_F(TextAnalysisSourceTest, TestGetLocaleName) {
+ UINT32 length = 0;
+ const wchar_t* locale_name = nullptr;
+ HRESULT hr = E_FAIL;
+ hr = source_->GetLocaleName(0, &length, &locale_name);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(kTextLength, length);
+ EXPECT_STREQ(kLocale, locale_name);
+
+ // Attempting to get a locale for a location that does not have a text chunk
+ // should fail.
+ hr = source_->GetLocaleName(kTextLength, &length, &locale_name);
+
+ EXPECT_TRUE(FAILED(hr));
+}
+
+TEST_F(TextAnalysisSourceTest, TestGetNumberSubstitution) {
+ UINT32 length = 0;
+ mswr::ComPtr<IDWriteNumberSubstitution> number_substitution;
+ HRESULT hr = E_FAIL;
+ hr = source_->GetNumberSubstitution(0, &length, &number_substitution);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(kTextLength, length);
+ EXPECT_NE(nullptr, number_substitution.Get());
+
+ // Attempting to get a number substitution for a location that does not have
+ // a text chunk should fail.
+ hr = source_->GetNumberSubstitution(kTextLength, &length,
+ &number_substitution);
+
+ EXPECT_TRUE(FAILED(hr));
+}
+
+TEST_F(TextAnalysisSourceTest, TestGetParagraphReadingDirection) {
+ EXPECT_EQ(kReadingDirection, source_->GetParagraphReadingDirection());
+}
+
+TEST_F(TextAnalysisSourceTest, TestGetTextAtPosition) {
+ UINT32 length = 0;
+ const wchar_t* text = nullptr;
+ HRESULT hr = E_FAIL;
+ hr = source_->GetTextAtPosition(0, &text, &length);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(kTextLength, length);
+ EXPECT_STREQ(kText, text);
+
+ hr = source_->GetTextAtPosition(5, &text, &length);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(kTextLength - 5, length);
+ EXPECT_STREQ(kText + 5, text);
+
+ // Trying to get a text chunk past the end should return null.
+ hr = source_->GetTextAtPosition(kTextLength, &text, &length);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(nullptr, text);
+}
+
+TEST_F(TextAnalysisSourceTest, TestGetTextBeforePosition) {
+ UINT32 length = 0;
+ const wchar_t* text = nullptr;
+ HRESULT hr = E_FAIL;
+ // Trying to get a text chunk before the beginning should return null.
+ hr = source_->GetTextBeforePosition(0, &text, &length);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(nullptr, text);
+
+ hr = source_->GetTextBeforePosition(5, &text, &length);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(5u, length);
+ EXPECT_STREQ(kText, text);
+
+ hr = source_->GetTextBeforePosition(kTextLength, &text, &length);
+
+ EXPECT_TRUE(SUCCEEDED(hr));
+ EXPECT_EQ(kTextLength, length);
+ EXPECT_STREQ(kText, text);
+}
+
+} // namespace win
+} // namespace gfx
diff --git a/ui/gfx/win/window_impl.cc b/ui/gfx/win/window_impl.cc
new file mode 100644
index 0000000..0fdf80b
--- /dev/null
+++ b/ui/gfx/win/window_impl.cc
@@ -0,0 +1,317 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/win/window_impl.h"
+
+#include <list>
+
+#include "base/bind.h"
+#include "base/cxx17_backports.h"
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
+#include "base/win/win_util.h"
+#include "base/win/wrapped_window_proc.h"
+#include "ui/gfx/win/crash_id_helper.h"
+#include "ui/gfx/win/hwnd_util.h"
+
+namespace gfx {
+
+static const DWORD kWindowDefaultChildStyle =
+ WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+static const DWORD kWindowDefaultStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowImpl class tracking.
+
+// Several external scripts rely explicitly on this base class name for
+// acquiring the window handle and will break if this is modified!
+// static
+const wchar_t* const WindowImpl::kBaseClassName = L"Chrome_WidgetWin_";
+
+// WindowImpl class information used for registering unique windows.
+struct ClassInfo {
+ ClassInfo(int style, HICON icon, HICON small_icon)
+ : style(style), icon(icon), small_icon(small_icon) {}
+
+ // Compares two ClassInfos. Returns true if all members match.
+ bool Equals(const ClassInfo& other) const {
+ return (other.style == style && other.icon == icon &&
+ other.small_icon == small_icon);
+ }
+
+ UINT style;
+ HICON icon;
+ HICON small_icon;
+};
+
+// WARNING: this class may be used on multiple threads.
+class ClassRegistrar {
+ public:
+ ClassRegistrar(const ClassRegistrar&) = delete;
+ ClassRegistrar& operator=(const ClassRegistrar&) = delete;
+
+ ~ClassRegistrar();
+
+ static ClassRegistrar* GetInstance();
+
+ void UnregisterClasses();
+
+ // Returns the atom identifying the class matching |class_info|,
+ // creating and registering a new class if the class is not yet known.
+ ATOM RetrieveClassAtom(const ClassInfo& class_info);
+
+ private:
+ // Represents a registered window class.
+ struct RegisteredClass {
+ RegisteredClass(const ClassInfo& info,
+ const std::wstring& name,
+ ATOM atom,
+ HINSTANCE instance);
+
+ // Info used to create the class.
+ ClassInfo info;
+
+ // The name given to the window class
+ std::wstring name;
+
+ // The atom identifying the window class.
+ ATOM atom;
+
+ // The handle of the module containing the window proceedure.
+ HMODULE instance;
+ };
+
+ ClassRegistrar();
+ friend struct base::DefaultSingletonTraits<ClassRegistrar>;
+
+ typedef std::list<RegisteredClass> RegisteredClasses;
+ RegisteredClasses registered_classes_;
+
+ // Counter of how many classes have been registered so far.
+ int registered_count_;
+
+ base::Lock lock_;
+};
+
+ClassRegistrar::~ClassRegistrar() {}
+
+// static
+ClassRegistrar* ClassRegistrar::GetInstance() {
+ return base::Singleton<ClassRegistrar,
+ base::LeakySingletonTraits<ClassRegistrar>>::get();
+}
+
+void ClassRegistrar::UnregisterClasses() {
+ for (RegisteredClasses::iterator i = registered_classes_.begin();
+ i != registered_classes_.end(); ++i) {
+ if (UnregisterClass(MAKEINTATOM(i->atom), i->instance)) {
+ registered_classes_.erase(i);
+ } else {
+ LOG(ERROR) << "Failed to unregister class " << i->name
+ << ". Error = " << GetLastError();
+ }
+ }
+}
+
+ATOM ClassRegistrar::RetrieveClassAtom(const ClassInfo& class_info) {
+ base::AutoLock auto_lock(lock_);
+ for (RegisteredClasses::const_iterator i = registered_classes_.begin();
+ i != registered_classes_.end(); ++i) {
+ if (class_info.Equals(i->info))
+ return i->atom;
+ }
+
+ // No class found, need to register one.
+ std::wstring name = std::wstring(WindowImpl::kBaseClassName) +
+ base::NumberToWString(registered_count_++);
+
+ WNDCLASSEX window_class;
+ base::win::InitializeWindowClass(
+ name.c_str(), &base::win::WrappedWindowProc<WindowImpl::WndProc>,
+ class_info.style, 0, 0, NULL,
+ reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), NULL,
+ class_info.icon, class_info.small_icon, &window_class);
+ HMODULE instance = window_class.hInstance;
+ ATOM atom = RegisterClassEx(&window_class);
+ if (!atom) {
+ // Perhaps the Window session has run out of atoms; see
+ // https://crbug.com/653493.
+ auto last_error = ::GetLastError();
+ base::debug::Alias(&last_error);
+ wchar_t name_copy[64];
+ base::wcslcpy(name_copy, name.c_str(), base::size(name_copy));
+ base::debug::Alias(name_copy);
+ PCHECK(atom);
+ }
+
+ registered_classes_.push_back(RegisteredClass(
+ class_info, name, atom, instance));
+
+ return atom;
+}
+
+ClassRegistrar::RegisteredClass::RegisteredClass(const ClassInfo& info,
+ const std::wstring& name,
+ ATOM atom,
+ HMODULE instance)
+ : info(info), name(name), atom(atom), instance(instance) {}
+
+ClassRegistrar::ClassRegistrar() : registered_count_(0) {}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowImpl, public
+
+WindowImpl::WindowImpl(const std::string& debugging_id)
+ : debugging_id_(debugging_id), class_style_(CS_DBLCLKS) {}
+
+WindowImpl::~WindowImpl() {
+ ClearUserData();
+}
+
+// static
+void WindowImpl::UnregisterClassesAtExit() {
+ base::AtExitManager::RegisterTask(
+ base::BindOnce(&ClassRegistrar::UnregisterClasses,
+ base::Unretained(ClassRegistrar::GetInstance())));
+}
+
+void WindowImpl::Init(HWND parent, const Rect& bounds) {
+ if (window_style_ == 0)
+ window_style_ = parent ? kWindowDefaultChildStyle : kWindowDefaultStyle;
+
+ if (parent == HWND_DESKTOP) {
+ // Only non-child windows can have HWND_DESKTOP (0) as their parent.
+ CHECK((window_style_ & WS_CHILD) == 0);
+ parent = GetWindowToParentTo(false);
+ } else if (parent == ::GetDesktopWindow()) {
+ // Any type of window can have the "Desktop Window" as their parent.
+ parent = GetWindowToParentTo(true);
+ } else if (parent != HWND_MESSAGE) {
+ CHECK(::IsWindow(parent));
+ }
+
+ int x, y, width, height;
+ if (bounds.IsEmpty()) {
+ x = y = width = height = CW_USEDEFAULT;
+ } else {
+ x = bounds.x();
+ y = bounds.y();
+ width = bounds.width();
+ height = bounds.height();
+ }
+
+ ATOM atom = GetWindowClassAtom();
+ auto weak_this = weak_factory_.GetWeakPtr();
+ HWND hwnd = CreateWindowEx(window_ex_style_,
+ reinterpret_cast<wchar_t*>(atom), NULL,
+ window_style_, x, y, width, height,
+ parent, NULL, NULL, this);
+ const DWORD create_window_error = ::GetLastError();
+
+ // First nccalcszie (during CreateWindow) for captioned windows is
+ // deliberately ignored so force a second one here to get the right
+ // non-client set up.
+ if (hwnd && (window_style_ & WS_CAPTION)) {
+ SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
+ SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
+ }
+
+ if (!hwnd_ && create_window_error == 0) {
+ bool still_alive = !!weak_this;
+ base::debug::Alias(&still_alive);
+ base::debug::Alias(&hwnd);
+ base::debug::Alias(&atom);
+ bool got_create = got_create_;
+ base::debug::Alias(&got_create);
+ bool got_valid_hwnd = got_valid_hwnd_;
+ base::debug::Alias(&got_valid_hwnd);
+ WNDCLASSEX class_info;
+ memset(&class_info, 0, sizeof(WNDCLASSEX));
+ class_info.cbSize = sizeof(WNDCLASSEX);
+ BOOL got_class = GetClassInfoEx(GetModuleHandle(NULL),
+ reinterpret_cast<wchar_t*>(atom),
+ &class_info);
+ base::debug::Alias(&got_class);
+ bool procs_match = got_class && class_info.lpfnWndProc ==
+ base::win::WrappedWindowProc<&WindowImpl::WndProc>;
+ base::debug::Alias(&procs_match);
+ CHECK(false);
+ }
+
+ CheckWindowCreated(hwnd_, create_window_error);
+
+ // The window procedure should have set the data for us.
+ CHECK_EQ(this, GetWindowUserData(hwnd));
+}
+
+HICON WindowImpl::GetDefaultWindowIcon() const {
+ return nullptr;
+}
+
+HICON WindowImpl::GetSmallWindowIcon() const {
+ return nullptr;
+}
+
+LRESULT WindowImpl::OnWndProc(UINT message, WPARAM w_param, LPARAM l_param) {
+ LRESULT result = 0;
+
+ HWND hwnd = hwnd_;
+ if (message == WM_NCDESTROY)
+ hwnd_ = nullptr;
+
+ // Handle the message if it's in our message map; otherwise, let the system
+ // handle it.
+ if (!ProcessWindowMessage(hwnd, message, w_param, l_param, result))
+ result = DefWindowProc(hwnd, message, w_param, l_param);
+
+ return result;
+}
+
+void WindowImpl::ClearUserData() {
+ if (::IsWindow(hwnd_))
+ gfx::SetWindowUserData(hwnd_, nullptr);
+}
+
+// static
+LRESULT CALLBACK WindowImpl::WndProc(HWND hwnd,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ WindowImpl* window = nullptr;
+ if (message == WM_NCCREATE) {
+ CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(l_param);
+ window = reinterpret_cast<WindowImpl*>(cs->lpCreateParams);
+ DCHECK(window);
+ gfx::SetWindowUserData(hwnd, window);
+ window->hwnd_ = hwnd;
+ window->got_create_ = true;
+ if (hwnd)
+ window->got_valid_hwnd_ = true;
+ } else {
+ window = reinterpret_cast<WindowImpl*>(GetWindowUserData(hwnd));
+ }
+
+ if (!window)
+ return 0;
+
+ auto logger =
+ CrashIdHelper::Get()->OnWillProcessMessages(window->debugging_id_);
+ return window->OnWndProc(message, w_param, l_param);
+}
+
+ATOM WindowImpl::GetWindowClassAtom() {
+ HICON icon = GetDefaultWindowIcon();
+ HICON small_icon = GetSmallWindowIcon();
+ ClassInfo class_info(initial_class_style(), icon, small_icon);
+ return ClassRegistrar::GetInstance()->RetrieveClassAtom(class_info);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/win/window_impl.h b/ui/gfx/win/window_impl.h
new file mode 100644
index 0000000..2a5683b
--- /dev/null
+++ b/ui/gfx/win/window_impl.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_WIN_WINDOW_IMPL_H_
+#define UI_GFX_WIN_WINDOW_IMPL_H_
+
+#include <string>
+
+#include "base/check_op.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/win/msg_util.h"
+
+namespace gfx {
+
+// An interface implemented by classes that use message maps.
+// ProcessWindowMessage is implemented by the BEGIN_MESSAGE_MAP_EX macro.
+class MessageMapInterface {
+ public:
+ // Processes one message from the window's message queue.
+ virtual BOOL ProcessWindowMessage(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT& result,
+ DWORD msg_map_id = 0) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// WindowImpl
+// A convenience class that encapsulates the details of creating and
+// destroying a HWND. This class also hosts the windows procedure used by all
+// Windows.
+//
+///////////////////////////////////////////////////////////////////////////////
+class GFX_EXPORT WindowImpl : public MessageMapInterface {
+ public:
+ // |debugging_id| is reported with crashes to help attribute the code that
+ // created the WindowImpl.
+ explicit WindowImpl(const std::string& debugging_id = std::string());
+
+ WindowImpl(const WindowImpl&) = delete;
+ WindowImpl& operator=(const WindowImpl&) = delete;
+
+ virtual ~WindowImpl();
+
+ // Causes all generated windows classes to be unregistered at exit.
+ // This can cause result in errors for tests that don't destroy all instances
+ // of windows, but is necessary if the tests unload the classes WndProc.
+ static void UnregisterClassesAtExit();
+
+ // Initializes the Window with a parent and an initial desired size.
+ void Init(HWND parent, const gfx::Rect& bounds);
+
+ // Returns the default window icon to use for windows of this type.
+ virtual HICON GetDefaultWindowIcon() const;
+ virtual HICON GetSmallWindowIcon() const;
+
+ // Returns the HWND associated with this Window.
+ HWND hwnd() const { return hwnd_; }
+
+ // Sets the window styles. This is ONLY used when the window is created.
+ // In other words, if you invoke this after invoking Init, nothing happens.
+ void set_window_style(DWORD style) { window_style_ = style; }
+ DWORD window_style() const { return window_style_; }
+
+ // Sets the extended window styles. See comment about |set_window_style|.
+ void set_window_ex_style(DWORD style) { window_ex_style_ = style; }
+ DWORD window_ex_style() const { return window_ex_style_; }
+
+ // Sets the class style to use. The default is CS_DBLCLKS.
+ void set_initial_class_style(UINT class_style) {
+ // We dynamically generate the class name, so don't register it globally!
+ DCHECK_EQ((class_style & CS_GLOBALCLASS), 0u);
+ class_style_ = class_style;
+ }
+ UINT initial_class_style() const { return class_style_; }
+
+ const std::string& debugging_id() const { return debugging_id_; }
+
+ protected:
+ // Handles the WndProc callback for this object.
+ virtual LRESULT OnWndProc(UINT message, WPARAM w_param, LPARAM l_param);
+
+ // Subclasses must call this method from their destructors to ensure that
+ // this object is properly disassociated from the HWND during destruction,
+ // otherwise it's possible this object may still exist while a subclass is
+ // destroyed.
+ void ClearUserData();
+
+ private:
+ friend class ClassRegistrar;
+
+ // The window procedure used by all Windows.
+ static LRESULT CALLBACK WndProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param);
+
+ // Gets the window class atom to use when creating the corresponding HWND.
+ // If necessary, this registers the window class.
+ ATOM GetWindowClassAtom();
+
+ // All classes registered by WindowImpl start with this name.
+ static const wchar_t* const kBaseClassName;
+
+ const std::string debugging_id_;
+
+ // Window Styles used when creating the window.
+ DWORD window_style_ = 0;
+
+ // Window Extended Styles used when creating the window.
+ DWORD window_ex_style_ = 0;
+
+ // Style of the class to use.
+ UINT class_style_;
+
+ // Our hwnd.
+ HWND hwnd_ = nullptr;
+
+ // For debugging.
+ // TODO(sky): nuke this when get crash data.
+ bool got_create_ = false;
+ bool got_valid_hwnd_ = false;
+ // For tracking whether this object has been destroyed. Must be last.
+ base::WeakPtrFactory<WindowImpl> weak_factory_{this};
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_WIN_WINDOW_IMPL_H_