#include "ui/gfx/win/hwnd_util.h"
#include <windows.h>
#include <dwmapi.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/scoped_gdi_object.h"
#include "base/win/win_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace gfx {
namespace {
void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
if (fit_to_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) {
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 {
NOTREACHED() << "Unable to find default monitor";
}
}
::SetWindowPos(hwnd, 0, bounds.left, bounds.top,
bounds.right - bounds.left, bounds.bottom - bounds.top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
NOINLINE void CrashAccessDenied(DWORD last_error) {
LOG(FATAL) << last_error;
}
NOINLINE void CrashOther(DWORD last_error) {
LOG(FATAL) << last_error;
}
}
std::wstring GetClassName(HWND window) {
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();
}
#pragma warning(push)
#pragma warning(disable:4312 4244)
WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
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);
if (process_id != ::GetCurrentProcessId())
return NULL;
return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
bool IsWindowCloaked(HWND hwnd) {
BOOL is_cloaked = FALSE;
return SUCCEEDED(DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &is_cloaked,
sizeof(is_cloaked))) &&
is_cloaked;
}
bool IsWindowVisibleAndFullyOpaque(HWND hwnd, Rect* window_rect) {
if (!IsWindow(hwnd) || !IsWindowVisible(hwnd)) {
return false;
}
if (IsIconic(hwnd)) {
return false;
}
LONG ex_styles = ::GetWindowLong(hwnd, GWL_EXSTYLE);
if (ex_styles & WS_EX_TRANSPARENT) {
return false;
}
if (ex_styles & WS_EX_TOOLWINDOW) {
if (GetClassName(hwnd) != L"Shell_TrayWnd") {
return false;
}
}
if (ex_styles & WS_EX_LAYERED) {
BYTE alpha;
DWORD flags;
if (!GetLayeredWindowAttributes(hwnd, nullptr, &alpha, &flags)) {
return false;
}
if (flags & LWA_ALPHA && alpha < 255) {
return false;
}
if (flags & LWA_COLORKEY) {
return false;
}
}
base::win::ScopedGDIObject<HRGN> region(CreateRectRgn(0, 0, 0, 0));
if (GetWindowRgn(hwnd, region.get()) == COMPLEXREGION) {
return false;
}
if (IsWindowCloaked(hwnd)) {
return false;
}
RECT win_rect;
if (!GetWindowRect(hwnd, &win_rect)) {
return false;
}
if (IsRectEmpty(&win_rect)) {
return false;
}
if (::GetWindowLong(hwnd, GWL_STYLE) & WS_POPUP) {
std::wstring hwnd_class_name = gfx::GetClassName(hwnd);
if (!hwnd_class_name.starts_with(L"Chrome_WidgetWin_") &&
hwnd_class_name != L"Shell_TrayWnd") {
return false;
}
}
if (window_rect) {
*window_rect = Rect(win_rect);
WINDOWPLACEMENT window_placement = {0};
window_placement.length = sizeof(WINDOWPLACEMENT);
::GetWindowPlacement(hwnd, &window_placement);
if (window_placement.showCmd == SW_MAXIMIZE) {
HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (hmon) {
MONITORINFO mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hmon, &mi)) {
(*window_rect).AdjustToFit(gfx::Rect(mi.rcWork));
}
}
}
}
return true;
}
std::optional<bool> IsWindowOnCurrentVirtualDesktop(
HWND window,
Microsoft::WRL::ComPtr<IVirtualDesktopManager> virtual_desktop_manager) {
BOOL on_current_desktop;
if (FAILED(virtual_desktop_manager->IsWindowOnCurrentVirtualDesktop(
window, &on_current_desktop))) {
return std::nullopt;
}
if (on_current_desktop)
return true;
GUID workspace_guid;
if (FAILED(virtual_desktop_manager->GetWindowDesktopId(window,
&workspace_guid))) {
return std::nullopt;
}
return workspace_guid == GUID_NULL;
}
#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);
RECT window_bounds;
RECT center_bounds = {0};
if (parent) {
::GetWindowRect(parent, ¢er_bounds);
}
if (::IsRectEmpty(¢er_bounds)) {
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 (::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:
case ERROR_NO_MORE_USER_HANDLES:
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;
}
}