// 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 "base/i18n/rtl.h" #include "base/strings/string_util.h" #include "base/win/win_util.h" #include "ui/gfx/geometry/point.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); } // Turn off optimizations for these functions so they show up in crash reports. MSVC_DISABLE_OPTIMIZE(); void CrashOutOfMemory() { PLOG(FATAL); } void CrashAccessDenied() { PLOG(FATAL); } // Crash isn't one of the ones we commonly see. void CrashOther() { PLOG(FATAL); } MSVC_ENABLE_OPTIMIZE(); } // namespace base::string16 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(GetWindowLongPtr(hwnd, GWLP_WNDPROC)); SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast(proc)); return oldwindow_proc; } void* SetWindowUserData(HWND hwnd, void* user_data) { return reinterpret_cast(SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(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(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) { if (!hwnd) { switch (GetLastError()) { case ERROR_NOT_ENOUGH_MEMORY: CrashOutOfMemory(); break; case ERROR_ACCESS_DENIED: CrashAccessDenied(); break; default: CrashOther(); break; } PLOG(FATAL); } } void ShowSystemMenu(HWND window) { RECT rect; GetWindowRect(window, &rect); Point point = Point(base::i18n::IsRTL() ? rect.right : rect.left, rect.top); static const int kSystemMenuOffset = 10; point.Offset(base::i18n::IsRTL() ? -kSystemMenuOffset : kSystemMenuOffset, kSystemMenuOffset); ShowSystemMenuAtPoint(window, point); } void ShowSystemMenuAtPoint(HWND window, const Point& point) { UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD; if (base::i18n::IsRTL()) flags |= TPM_RIGHTALIGN; HMENU menu = GetSystemMenu(window, FALSE); const int command = TrackPopupMenu(menu, flags, point.x(), point.y(), 0, window, NULL); if (command) SendMessage(window, WM_SYSCOMMAND, command, 0); } extern "C" { typedef HWND (*RootWindow)(); } HWND GetWindowToParentTo(bool get_real_hwnd) { return get_real_hwnd ? ::GetDesktopWindow() : HWND_DESKTOP; } } // namespace gfx