sbWindowChromeService.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 #include "sbWindowChromeService.h"
26 
27 #include <prlog.h>
28 
33 #ifdef PR_LOGGING
34 static PRLogModuleInfo* gWindowChromeServiceLog = nsnull;
35 #define TRACE(args) PR_LOG(gWindowChromeServiceLog, PR_LOG_DEBUG, args)
36 #define LOG(args) PR_LOG(gWindowChromeServiceLog, PR_LOG_WARN, args)
37 #else
38 #define TRACE(args) /* nothing */
39 #define LOG(args) /* nothing */
40 #endif
41 
42 
43 #include <commctrl.h>
44 #include <shellapi.h>
45 #include <Uxtheme.h>
46 
47 #include "../NativeWindowFromNode.h"
48 
49 typedef HRESULT (WINAPI *t_DwmIsCompositionEnabled)(BOOL *);
50 
51 static UINT_PTR gSubclassId = NULL;
52 
54 
56  : mhDWMAPI(NULL),
57  mDwmIsCompositionEnabled(NULL)
58 {
59 #ifdef PR_LOGGING
60  if (!gWindowChromeServiceLog) {
61  gWindowChromeServiceLog = PR_NewLogModule("sbWindowChromeService");
62  }
63 
64  TRACE(("sbWindowChromeService[0x%x] - ctor", this));
65 #endif
66 
67  NS_ASSERTION(!gSubclassId,
68  "sbWindowChromeService constructed twice!");
69  if (!gSubclassId) {
70  gSubclassId = (UINT_PTR)this;
71  mhDWMAPI = ::LoadLibrary(TEXT("dwmapi"));
72  if (mhDWMAPI) {
74  (t_DwmIsCompositionEnabled)::GetProcAddress(mhDWMAPI,
75  "DwmIsCompositionEnabled");
76  }
77  }
78 }
79 
80 sbWindowChromeService::~sbWindowChromeService()
81 {
82  if (gSubclassId == (UINT_PTR)this) {
83  gSubclassId = NULL;
84  }
85  if (mhDWMAPI) {
86  ::FreeLibrary(mhDWMAPI);
87  }
88 }
89 
90 /* static */ bool
92 {
93  if (!self || !self->mDwmIsCompositionEnabled) {
94  // DWM API isn't available
95  return false;
96  }
97  BOOL isDWMCompositionEnabled = FALSE;
98  HRESULT hr = self->mDwmIsCompositionEnabled(&isDWMCompositionEnabled);
99  if (FAILED(hr)) {
100  return false;
101  }
102 
103  return isDWMCompositionEnabled != FALSE;
104 }
105 
106 /* void hideChrome (in nsISupports aWindow, in boolean aHide); */
107 NS_IMETHODIMP sbWindowChromeService::HideChrome(nsISupports *aWindow,
108  PRBool aHide)
109 {
110  NS_ENSURE_ARG_POINTER(aWindow);
111  NS_ENSURE_STATE(gSubclassId == (UINT_PTR)this);
112 
113  HWND hWnd = NativeWindowFromNode::get(aWindow);
114  NS_ENSURE_TRUE(hWnd, NS_ERROR_INVALID_ARG);
115 
116  if (aHide) {
117  LONG style = ::GetWindowLong(hWnd, GWL_STYLE);
118  BOOL success = ::SetWindowSubclass(hWnd,
120  gSubclassId,
121  style);
122  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
123  // We need to set WS_CAPTION, to make Windows think that we are a real
124  // window (which leads to things like auto-hiding taskbars working properly
125  // and having a taskbar button).
126  style |= WS_CAPTION;
127  ::SetWindowLong(hWnd, GWL_STYLE, style);
128  }
129  else {
130  DWORD_PTR oldStyle;
131  BOOL wasSubclassed = ::GetWindowSubclass(hWnd,
133  gSubclassId,
134  &oldStyle);
135  if (wasSubclassed) {
136  ::SetWindowLong(hWnd, GWL_STYLE, oldStyle);
137  BOOL success = ::RemoveWindowSubclass(hWnd,
139  gSubclassId);
140  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
141  }
142  }
143  return NS_OK;
144 }
145 
146 /* readonly attribute boolean isCompositionEnabled; */
147 NS_IMETHODIMP
148 sbWindowChromeService::GetIsCompositionEnabled(PRBool *aIsCompositionEnabled)
149 {
150  NS_ENSURE_ARG_POINTER(aIsCompositionEnabled);
151  *aIsCompositionEnabled = IsCompositionEnabled(this);
152  return NS_OK;
153 }
154 
155 /* static */
156 LRESULT
158  UINT uMsg,
159  WPARAM wParam,
160  LPARAM lParam,
161  UINT_PTR uIdSubclass,
162  DWORD_PTR dwRefData)
163 {
164  if (uIdSubclass != gSubclassId) {
165  // We're majorly screwed up somehow; do nothing
166  return DefSubclassProc(hWnd, uMsg, wParam, lParam);
167  }
168  TRACE(("%s: WndProc(%p, %08x)", __FUNCTION__, hWnd, uMsg));
169  switch (uMsg) {
170  case WM_NCDESTROY:
171  {
172  // clean up
173  RemoveWindowSubclass(hWnd, &sbWindowChromeService::WndProc, uIdSubclass);
174  break;
175  }
176  case WM_NCCALCSIZE:
177  {
178  if (!(BOOL)wParam) {
179  // we do not need to do anything special for this case
180  break;
181  }
182  NCCALCSIZE_PARAMS* params = (NCCALCSIZE_PARAMS*)lParam;
183  bool willMinimize = (params->rgrc[0].left == -32000) &&
184  (params->rgrc[0].top == -32000);
185  if (willMinimize) {
186  // we are going to minimize; don't touch anything
187  break;
188  }
189  if (IsZoomed(hWnd)) {
190  // On Windows 7, Windows will adjust the size of the maximized windows
191  // so that they're larger than the screen to account for the window
192  // border (even with Aero disabled). On XP, however, it does _not_ do
193  // that. So, figure out how big the window wants to be, and how big the
194  // screen is, and go for the closest solution.
195  RECT* rectWindow = &params->rgrc[0]; // shorthand
196  RECT rectMon = {0};
197  int xSize = ::GetSystemMetrics(SM_CXSIZEFRAME);
198  int ySize = ::GetSystemMetrics(SM_CYSIZEFRAME);
199  // Bug 21344 - HMONITOR's are not handles
200  HMONITOR hMon = ::MonitorFromRect(rectWindow, MONITOR_DEFAULTTONULL);
201  if (hMon) {
202  MONITORINFO monInfo = {0};
203  monInfo.cbSize = sizeof(monInfo);
204  BOOL success = ::GetMonitorInfo(hMon, &monInfo);
205  if (success) {
206  ::CopyRect(&rectMon, &monInfo.rcWork);
207  }
208  else {
209  ::CopyRect(&rectMon, rectWindow);
210  }
211  }
212  else {
213  // no monitor!?
214  ::CopyRect(&rectMon, rectWindow);
215  }
216 
217  const LONG TOLERANCE = 2;
218  if (rectMon.top > rectWindow->top &&
219  rectMon.top - rectWindow->top < ySize + TOLERANCE)
220  {
221  rectWindow->top += ySize;
222  }
223  if (rectWindow->bottom > rectMon.bottom &&
224  rectWindow->bottom - rectMon.bottom < ySize + TOLERANCE)
225  {
226  rectWindow->bottom -= ySize;
227  }
228  if (rectMon.left > rectWindow->left &&
229  rectMon.left - rectWindow->left < xSize + TOLERANCE)
230  {
231  rectWindow->left += xSize;
232  }
233  if (rectWindow->right > rectMon.right &&
234  rectWindow->right - rectMon.right < xSize + TOLERANCE)
235  {
236  rectWindow->right -= xSize;
237  }
238 
239  // Chop off one pixel on any edge with auto-hidden taskbars (deskbars,
240  // really). This is required to make sure those taskbars will remain
241  // functional (i.e. pop up) when the mouse goes near them - otherwise
242  // Windows assumes we really meant to be a full screen window instead.
243  APPBARDATA appbarData = {sizeof(APPBARDATA)};
244  appbarData.hWnd = hWnd;
245  HWND wnd;
246  // Bug 21344 - HMONITOR's are not handles
247  HMONITOR selfMon = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST),
248  targetMon;
249 
250  appbarData.uEdge = ABE_TOP;
251  wnd = (HWND)::SHAppBarMessage(ABM_GETAUTOHIDEBAR, &appbarData);
252  if (wnd) {
253  targetMon = ::MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST);
254  if (targetMon == selfMon) {
255  // instead of bumping the window down, subtract a pixel from the
256  // bottom instead - otherwise we end up exposing a pixel of the
257  // top which 1) can lead to clicking on nearly-invisible widgets (e.g.
258  // the close button), 2) loses fitt's law gains of having things at top
259  params->rgrc[0].bottom -= 1;
260  }
261  }
262  appbarData.uEdge = ABE_RIGHT;
263  wnd = (HWND)::SHAppBarMessage(ABM_GETAUTOHIDEBAR, &appbarData);
264  if (wnd) {
265  targetMon = ::MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST);
266  if (targetMon == selfMon) {
267  params->rgrc[0].right -= 1;
268  }
269  }
270  appbarData.uEdge = ABE_BOTTOM;
271  wnd = (HWND)::SHAppBarMessage(ABM_GETAUTOHIDEBAR, &appbarData);
272  if (wnd) {
273  targetMon = ::MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST);
274  if (targetMon == selfMon) {
275  params->rgrc[0].bottom -= 1;
276  }
277  }
278  appbarData.uEdge = ABE_LEFT;
279  wnd = (HWND)::SHAppBarMessage(ABM_GETAUTOHIDEBAR, &appbarData);
280  if (wnd) {
281  targetMon = ::MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST);
282  if (targetMon == selfMon) {
283  params->rgrc[0].left += 1;
284  }
285  }
286  return 0;
287  }
288  // we have a window we want to hook, but it's not maximized; leave the
289  // rectangles as-is, meaning that there should be no non-client area (i.e.
290  // no chrome at all).
291  return 0;
292  }
293  case WM_NCACTIVATE:
294  {
295  if (IsCompositionEnabled((sbWindowChromeService*)uIdSubclass)) {
296  // When DWM composition is enabled, call the default handler so that
297  // it draws the window shadow. In this case the window is also double
298  // buffered (by the DWM), so we don't have to worry about flickering.
299 
300  // We skip doing this if we're maximized (or look like we are, since that
301  // is how Mozilla implemented full screen) to avoid problems with the
302  // video window showing the (win9x) non-client area. We don't need to
303  // have shadows in that case anyway.
304  RECT rectMon = {0}, rectWindow;
305  BOOL success = ::GetWindowRect(hWnd, &rectWindow);
306  if (!success) {
307  ::SetRect(&rectWindow, -1, -1, -1, -1);
308  }
309  // Bug 21344 - HMONITOR's are not handles
310  HMONITOR hMon = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL);
311  if (hMon) {
312  MONITORINFO monInfo = {0};
313  monInfo.cbSize = sizeof(monInfo);
314  success = ::GetMonitorInfo(hMon, &monInfo);
315  if (success) {
316  ::CopyRect(&rectMon, &monInfo.rcMonitor);
317  }
318  }
319  if (!::EqualRect(&rectMon, &rectWindow)) {
320  return DefSubclassProc(hWnd, uMsg, wParam, lParam);
321  }
322  }
323  else {
324  // Let's turn off themes to make sure our borders don't get rounded!
325  ::SetWindowTheme(hWnd, L"", L"");
326  }
327  // No DWM, don't do anything to avoid extra paints of the non-client area
328  // which causes bad flickering.
329  return TRUE;
330  }
331  case WM_NCPAINT:
332  {
333  TRACE(("WM_NCPAINT(%p)", hWnd));
334  // We need to call the default implementation in order to get window shadow.
335  // Since that only works when DWM is enabled anyway, check if it is - if it
336  // is not, do not call the default implementation since that causes flicker
337  // on systems with theming but not DWM (e.g. XP, or Vista with 16bpp).
338  if (!IsCompositionEnabled((sbWindowChromeService*)uIdSubclass)) {
339  // no need to do anything; however, invalidate the window anyway in case
340  // this paint was caused by invalidation.
341  InvalidateRgn(hWnd, NULL, FALSE);
342  return 0;
343  }
344  return DefSubclassProc(hWnd, uMsg, wParam, lParam);
345  }
346  case 0xAE: /* undocumented: WM_NCUAHDRAWCAPTION */
347  case 0xAF: /* undocumented: WM_NCUAHDRAWFRAME */
348  // these messages, according to chromium.org, are sent by Windows to redraw
349  // the caption / frame at unpredictable times. Intercept these and don't
350  // use the default behaviour.
351  return 0;
352  }
353  return DefSubclassProc(hWnd, uMsg, wParam, lParam);
354 }
HRESULT(WINAPI * t_DwmIsCompositionEnabled)(BOOL *)
return NS_OK
NS_IMPL_ISUPPORTS1(sbWindowChromeService, sbIWindowChromeService)
LONG left
Definition: MultiMonitor.h:48
static nsresult LoadLibrary(nsCOMPtr< nsIFile > aLibDir, nsString aLibPath)
static LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
t_DwmIsCompositionEnabled mDwmIsCompositionEnabled
HRESULT(WINAPI * t_DwmIsCompositionEnabled)(BOOL *)
static bool IsCompositionEnabled(const sbWindowChromeService *self)
LONG bottom
Definition: MultiMonitor.h:51
long LONG
Definition: MultiMonitor.h:38
Component used to do platform-level tricks to hide the window chrome. Expected to only be used by sys...
static UINT_PTR gSubclassId
LONG top
Definition: MultiMonitor.h:49
static void * get(nsISupports *window)
NS_DECL_ISUPPORTS NS_DECL_SBIWINDOWCHROMESERVICE sbWindowChromeService()
Element Properties style
LONG right
Definition: MultiMonitor.h:50
var self
#define TRACE(args)