1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/win/hwnd_util.h"
7 #include "base/i18n/rtl.h"
8 #include "base/strings/string_util.h"
9 #include "base/tracked_objects.h"
10 #include "base/win/metro.h"
11 #include "base/win/win_util.h"
12 #include "ui/gfx/geometry/point.h"
13 #include "ui/gfx/geometry/rect.h"
14 #include "ui/gfx/geometry/size.h"
20 // Adjust the window to fit.
21 void AdjustWindowToFit(HWND hwnd
, const RECT
& bounds
, bool fit_to_monitor
) {
24 HMONITOR hmon
= MonitorFromRect(&bounds
, MONITOR_DEFAULTTONEAREST
);
27 mi
.cbSize
= sizeof(mi
);
28 GetMonitorInfo(hmon
, &mi
);
29 Rect
window_rect(bounds
);
30 Rect
monitor_rect(mi
.rcWork
);
31 Rect new_window_rect
= window_rect
;
32 new_window_rect
.AdjustToFit(monitor_rect
);
33 if (new_window_rect
!= window_rect
) {
34 // Window doesn't fit on monitor, move and possibly resize.
35 SetWindowPos(hwnd
, 0, new_window_rect
.x(), new_window_rect
.y(),
36 new_window_rect
.width(), new_window_rect
.height(),
37 SWP_NOACTIVATE
| SWP_NOZORDER
);
42 NOTREACHED() << "Unable to find default monitor";
45 } // Else fall through.
47 // The window is not being fit to monitor, or the window fits on the monitor
48 // as is, or we have no monitor info; reset the bounds.
49 ::SetWindowPos(hwnd
, 0, bounds
.left
, bounds
.top
,
50 bounds
.right
- bounds
.left
, bounds
.bottom
- bounds
.top
,
51 SWP_NOACTIVATE
| SWP_NOZORDER
);
54 // Turn off optimizations for these functions so they show up in crash reports.
55 MSVC_DISABLE_OPTIMIZE();
57 void CrashOutOfMemory() {
61 void CrashAccessDenied() {
65 // Crash isn't one of the ones we commonly see.
70 MSVC_ENABLE_OPTIMIZE();
74 base::string16
GetClassName(HWND window
) {
75 // GetClassNameW will return a truncated result (properly null terminated) if
76 // the given buffer is not large enough. So, it is not possible to determine
77 // that we got the entire class name if the result is exactly equal to the
78 // size of the buffer minus one.
79 DWORD buffer_size
= MAX_PATH
;
82 DWORD size_ret
= GetClassNameW(
83 window
, base::WriteInto(&output
, buffer_size
), buffer_size
);
86 if (size_ret
< (buffer_size
- 1)) {
87 output
.resize(size_ret
);
92 return std::wstring(); // error
96 #pragma warning(disable:4312 4244)
98 WNDPROC
SetWindowProc(HWND hwnd
, WNDPROC proc
) {
99 // The reason we don't return the SetwindowLongPtr() value is that it returns
100 // the orignal window procedure and not the current one. I don't know if it is
101 // a bug or an intended feature.
102 WNDPROC oldwindow_proc
=
103 reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hwnd
, GWLP_WNDPROC
));
104 SetWindowLongPtr(hwnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(proc
));
105 return oldwindow_proc
;
108 void* SetWindowUserData(HWND hwnd
, void* user_data
) {
110 reinterpret_cast<void*>(SetWindowLongPtr(hwnd
, GWLP_USERDATA
,
111 reinterpret_cast<LONG_PTR
>(user_data
)));
114 void* GetWindowUserData(HWND hwnd
) {
115 DWORD process_id
= 0;
116 GetWindowThreadProcessId(hwnd
, &process_id
);
117 // A window outside the current process needs to be ignored.
118 if (process_id
!= ::GetCurrentProcessId())
120 return reinterpret_cast<void*>(GetWindowLongPtr(hwnd
, GWLP_USERDATA
));
125 bool DoesWindowBelongToActiveWindow(HWND window
) {
127 HWND top_window
= ::GetAncestor(window
, GA_ROOT
);
131 HWND active_top_window
= ::GetAncestor(::GetForegroundWindow(), GA_ROOT
);
132 return (top_window
== active_top_window
);
135 void CenterAndSizeWindow(HWND parent
,
138 DCHECK(window
&& pref
.width() > 0 && pref
.height() > 0);
140 // Calculate the ideal bounds.
142 RECT center_bounds
= {0};
144 // If there is a parent, center over the parents bounds.
145 ::GetWindowRect(parent
, ¢er_bounds
);
148 if (::IsRectEmpty(¢er_bounds
)) {
149 // No parent or no parent rect. Center over the monitor the window is on.
150 HMONITOR monitor
= MonitorFromWindow(window
, MONITOR_DEFAULTTONEAREST
);
152 MONITORINFO mi
= {0};
153 mi
.cbSize
= sizeof(mi
);
154 GetMonitorInfo(monitor
, &mi
);
155 center_bounds
= mi
.rcWork
;
157 NOTREACHED() << "Unable to get default monitor";
161 window_bounds
.left
= center_bounds
.left
;
162 if (pref
.width() < (center_bounds
.right
- center_bounds
.left
)) {
163 window_bounds
.left
+=
164 (center_bounds
.right
- center_bounds
.left
- pref
.width()) / 2;
166 window_bounds
.right
= window_bounds
.left
+ pref
.width();
168 window_bounds
.top
= center_bounds
.top
;
169 if (pref
.height() < (center_bounds
.bottom
- center_bounds
.top
)) {
171 (center_bounds
.bottom
- center_bounds
.top
- pref
.height()) / 2;
173 window_bounds
.bottom
= window_bounds
.top
+ pref
.height();
175 // If we're centering a child window, we are positioning in client
176 // coordinates, and as such we need to offset the target rectangle by the
177 // position of the parent window.
178 if (::GetWindowLong(window
, GWL_STYLE
) & WS_CHILD
) {
179 DCHECK(parent
&& ::GetParent(window
) == parent
);
180 POINT topleft
= { window_bounds
.left
, window_bounds
.top
};
181 ::MapWindowPoints(HWND_DESKTOP
, parent
, &topleft
, 1);
182 window_bounds
.left
= topleft
.x
;
183 window_bounds
.top
= topleft
.y
;
184 window_bounds
.right
= window_bounds
.left
+ pref
.width();
185 window_bounds
.bottom
= window_bounds
.top
+ pref
.height();
188 AdjustWindowToFit(window
, window_bounds
, !parent
);
191 void CheckWindowCreated(HWND hwnd
) {
193 switch (GetLastError()) {
194 case ERROR_NOT_ENOUGH_MEMORY
:
197 case ERROR_ACCESS_DENIED
:
208 void ShowSystemMenu(HWND window
) {
210 GetWindowRect(window
, &rect
);
211 Point point
= Point(base::i18n::IsRTL() ? rect
.right
: rect
.left
, rect
.top
);
212 static const int kSystemMenuOffset
= 10;
213 point
.Offset(base::i18n::IsRTL() ? -kSystemMenuOffset
: kSystemMenuOffset
,
215 ShowSystemMenuAtPoint(window
, point
);
218 void ShowSystemMenuAtPoint(HWND window
, const Point
& point
) {
219 // In the Metro process, we never want to show the system menu.
220 if (base::win::IsMetroProcess())
222 UINT flags
= TPM_LEFTBUTTON
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
;
223 if (base::i18n::IsRTL())
224 flags
|= TPM_RIGHTALIGN
;
225 HMENU menu
= GetSystemMenu(window
, FALSE
);
227 // Use task stopwatch to exclude the time while the context menu is open from
228 // the current task, if any.
229 tracked_objects::TaskStopwatch stopwatch
;
232 TrackPopupMenu(menu
, flags
, point
.x(), point
.y(), 0, window
, NULL
);
236 SendMessage(window
, WM_SYSCOMMAND
, command
, 0);
240 typedef HWND (*RootWindow
)();
243 HWND
GetWindowToParentTo(bool get_real_hwnd
) {
244 HMODULE metro
= base::win::GetMetroModule();
246 return get_real_hwnd
? ::GetDesktopWindow() : HWND_DESKTOP
;
247 // In windows 8 metro-mode the root window is not the desktop.
248 RootWindow root_window
=
249 reinterpret_cast<RootWindow
>(::GetProcAddress(metro
, "GetRootWindow"));
250 return root_window();