1
// Copyright 2010, Google Inc.
2
// All rights reserved.
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are
8
// * Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// * Redistributions in binary form must reproduce the above
11
// copyright notice, this list of conditions and the following disclaimer
12
// in the documentation and/or other materials provided with the
14
// * Neither the name of Google Inc. nor the names of its
15
// contributors may be used to endorse or promote products derived from
16
// this software without specific prior written permission.
18
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
#include "gui/base/win_util.h"
32
#include <QtCore/QFile>
33
#include <QtCore/QLibrary>
34
#include <QtCore/QList>
35
#include <QtCore/QPointer>
36
#include <QtGui/QApplication>
37
#include <QtGui/QDesktopWidget>
38
#include <QtGui/QPainter>
39
#include <QtGui/QPaintEngine>
40
#include <QtGui/QWidget>
42
#include "base/base.h"
43
#include "base/util.h"
44
#include "base/singleton.h"
52
#include <Qt/qt_windows.h>
54
#ifndef WM_DWMCOMPOSITIONCHANGED
55
#define WM_DWMCOMPOSITIONCHANGED 0x031E
56
#endif // WM_DWMCOMPOSITIONCHANGED
59
typedef HRESULT (WINAPI *FPDwmIsCompositionEnabled)
61
typedef HRESULT (WINAPI *FPDwmExtendFrameIntoClientArea)
63
const MARGINS *pMarInset);
64
typedef HRESULT (WINAPI *FPDwmEnableBlurBehindWindow)
66
const DWM_BLURBEHIND *pBlurBehind);
69
typedef HANDLE (WINAPI *FPOpenThemeData)
70
(HWND hwnd, LPCWSTR pszClassList);
71
typedef HRESULT (WINAPI *FPCloseThemeData)
73
typedef HRESULT (WINAPI *FPDrawThemeTextEx)
74
(HANDLE hTheme, HDC hdc, int iPartId, int iStateId,
75
LPCWSTR pszText, int cchText, DWORD dwTextFlags,
76
LPRECT pRect, const DTTOPTS *pOptions);
77
typedef HRESULT (WINAPI *FPGetThemeSysFont)
78
(HANDLE hTheme, int iFontId, LOGFONTW *plf);
88
FPDwmIsCompositionEnabled gDwmIsCompositionEnabled = NULL;
89
FPDwmEnableBlurBehindWindow gDwmEnableBlurBehindWindow = NULL;
90
FPDwmExtendFrameIntoClientArea gDwmExtendFrameIntoClientArea = NULL;
91
FPOpenThemeData gOpenThemeData = NULL;
92
FPCloseThemeData gCloseThemeData = NULL;
93
FPDrawThemeTextEx gDrawThemeTextEx = NULL;
94
FPGetThemeSysFont gGetThemeSysFont = NULL;
96
class WindowNotifier : public QWidget {
99
winId(); // to make a window handle
102
virtual ~WindowNotifier() {}
104
void AddWidget(QWidget *widget) {
105
widgets_.append(widget);
108
void RemoveWidget(QWidget *widget) {
109
widgets_.removeAll(widget);
112
bool winEvent(MSG *message, long *result);
114
void InstallStyleSheets(const QString &dwm_on_style,
115
const QString &dwm_off_style) {
116
dwm_on_style_ = dwm_on_style;
117
dwm_off_style_ = dwm_off_style;
120
static WindowNotifier *Get() {
121
return Singleton<WindowNotifier>::get();
125
QWidgetList widgets_;
126
QString dwm_on_style_;
127
QString dwm_off_style_;
132
DwmResolver() : dwmlib_(NULL), themelib_(NULL) {
133
dwmlib_ = Util::LoadSystemLibrary(L"dwmapi.dll");
134
themelib_ = Util::LoadSystemLibrary(L"uxtheme.dll");
136
if (NULL != dwmlib_) {
137
gDwmIsCompositionEnabled =
138
reinterpret_cast<FPDwmIsCompositionEnabled>
139
(::GetProcAddress(dwmlib_,
140
"DwmIsCompositionEnabled"));
141
gDwmExtendFrameIntoClientArea =
142
reinterpret_cast<FPDwmExtendFrameIntoClientArea>
143
(::GetProcAddress(dwmlib_,
144
"DwmExtendFrameIntoClientArea"));
145
gDwmEnableBlurBehindWindow =
146
reinterpret_cast<FPDwmEnableBlurBehindWindow>
147
(::GetProcAddress(dwmlib_,
148
"DwmEnableBlurBehindWindow"));
151
if (NULL != themelib_) {
153
reinterpret_cast<FPOpenThemeData>(
154
::GetProcAddress(themelib_, "OpenThemeData"));
156
reinterpret_cast<FPCloseThemeData>(
157
::GetProcAddress(themelib_, "CloseThemeData"));
159
reinterpret_cast<FPDrawThemeTextEx>(
160
::GetProcAddress(themelib_, "DrawThemeTextEx"));
162
reinterpret_cast<FPGetThemeSysFont>(
163
::GetProcAddress(themelib_, "GetThemeSysFont"));
167
bool IsAvailable() const {
168
return (gDwmIsCompositionEnabled != NULL &&
169
gDwmExtendFrameIntoClientArea != NULL &&
170
gDwmEnableBlurBehindWindow != NULL &&
171
gOpenThemeData != NULL &&
172
gCloseThemeData != NULL &&
173
gDrawThemeTextEx != NULL &&
174
gGetThemeSysFont != NULL);
177
static bool ResolveLibs() {
178
return Singleton<DwmResolver>::get()->IsAvailable();
188
bool WinUtil::IsCompositionEnabled() {
190
if (!DwmResolver::ResolveLibs()) {
195
BOOL is_enabled = false;
196
hr = gDwmIsCompositionEnabled(&is_enabled);
200
LOG(ERROR) << "DwmIsCompositionEnabled() failed: "
201
<< static_cast<long>(hr);
208
bool WinUtil::ExtendFrameIntoClientArea(QWidget *widget,
210
int right, int bottom) {
214
if (!DwmResolver::ResolveLibs()) {
219
MARGINS margin = { left, top, right, bottom };
220
hr = gDwmExtendFrameIntoClientArea(widget->winId(), &margin);
222
WindowNotifier::Get()->AddWidget(widget);
223
widget->setAttribute(Qt::WA_TranslucentBackground, true);
226
LOG(ERROR) << "DwmExtendFrameIntoClientArea() failed: "
227
<< static_cast<long>(hr);
235
bool WindowNotifier::winEvent(MSG *message, long *result) {
236
if (message != NULL && message->message == WM_DWMCOMPOSITIONCHANGED) {
237
const bool composition_enabled = WinUtil::IsCompositionEnabled();
239
// switch styles if need be
240
if (!dwm_on_style_.isEmpty() && !dwm_off_style_.isEmpty()) {
241
if (composition_enabled) {
242
qApp->setStyleSheet(dwm_on_style_);
244
qApp->setStyleSheet(dwm_off_style_);
248
foreach(QWidget *widget, widgets_) {
249
if (widget != NULL) {
250
widget->setAttribute(Qt::WA_NoSystemBackground,
251
composition_enabled);
252
// TODO(taku): left/top/right/bottom are not updated.
254
if (composition_enabled) {
255
MARGINS margin = { -1, 0, 0, 0 };
256
gDwmExtendFrameIntoClientArea(widget->winId(), &margin);
257
widget->setAttribute(Qt::WA_TranslucentBackground, true);
265
return QWidget::winEvent(message, result);
269
QRect WinUtil::GetTextRect(QWidget *widget, const QString &text) {
271
const QFont font = QApplication::font(widget);
272
const QFontMetrics fontMetrics(font);
273
return fontMetrics.boundingRect(text);
276
void WinUtil::InstallStyleSheets(const QString &dwm_on_style,
277
const QString &dwm_off_style) {
279
WindowNotifier::Get()->InstallStyleSheets(dwm_on_style,
284
void WinUtil::InstallStyleSheetsFiles(const QString &dwm_on_style_file,
285
const QString &dwm_off_style_file) {
287
QFile file1(dwm_on_style_file);
288
file1.open(QFile::ReadOnly);
289
QFile file2(dwm_off_style_file);
290
file2.open(QFile::ReadOnly);
291
WinUtil::InstallStyleSheets(QLatin1String(file1.readAll()),
292
QLatin1String(file2.readAll()));
296
void WinUtil::DrawThemeText(const QString &text,
303
if (!DwmResolver::ResolveLibs()) {
307
HDC hdc = painter->paintEngine()->getDC();
309
LOG(ERROR) << "hdc is NULL";
313
HANDLE theme = gOpenThemeData(qApp->desktop()->winId(), L"WINDOW");
315
LOG(ERROR) << "::OpenThemaData() failed";
319
// Set up a memory DC and bitmap that we'll draw into
320
HDC dc_mem = ::CreateCompatibleDC(hdc);
321
if (dc_mem == NULL) {
322
gCloseThemeData(theme);
323
LOG(ERROR) << "::CreateCompatibleDC() failed: " << ::GetLastError();
327
BITMAPINFO dib = { 0 };
328
dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
329
dib.bmiHeader.biWidth = rect.width();
330
dib.bmiHeader.biHeight = -rect.height();
331
dib.bmiHeader.biPlanes = 1;
332
dib.bmiHeader.biBitCount = 32;
333
dib.bmiHeader.biCompression = BI_RGB;
335
HBITMAP bmp = ::CreateDIBSection(hdc, &dib,
336
DIB_RGB_COLORS, NULL, NULL, 0);
339
gCloseThemeData(theme);
340
LOG(ERROR) << "::CreateDIBSection() failed: " << ::GetLastError();
346
gGetThemeSysFont(theme, TMT_CAPTIONFONT, &lf);
348
NONCLIENTMETRICS ncm = { sizeof(NONCLIENTMETRICS) };
349
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
350
sizeof(NONCLIENTMETRICS), &ncm, false);
351
lf = ncm.lfMessageFont;
354
HFONT caption_font = ::CreateFontIndirect(&lf);
355
HBITMAP old_bmp = reinterpret_cast<HBITMAP>(
356
::SelectObject(dc_mem,
357
reinterpret_cast<HGDIOBJ>(bmp)));
358
HFONT old_font = reinterpret_cast<HFONT>(
359
::SelectObject(dc_mem,
360
reinterpret_cast<HGDIOBJ>(caption_font)));
362
DTTOPTS dto = { sizeof(DTTOPTS) };
363
const UINT format = DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_NOPREFIX;
364
RECT rctext ={ 0, 0, rect.width(), rect.height() };
366
dto.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE;
367
dto.iGlowSize = glow_size;
370
gDrawThemeTextEx(theme, dc_mem, 0, 0,
371
reinterpret_cast<LPCWSTR>(text.utf16()),
372
-1, format, &rctext, &dto);
374
// Copy to the painter's HDC
375
if (!::BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(),
376
dc_mem, 0, 0, SRCCOPY)) {
377
LOG(ERROR) << "::BitBlt() failed: " << ::GetLastError();
380
LOG(ERROR) << "::DrawThemeTextEx() failed: " << static_cast<long>(hr);
383
::SelectObject(dc_mem, reinterpret_cast<HGDIOBJ>(old_bmp));
384
::SelectObject(dc_mem, reinterpret_cast<HGDIOBJ>(old_font));
386
::DeleteObject(caption_font);
388
gCloseThemeData(theme);
394
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lp) {
396
::GetWindowThreadProcessId(hwnd, &id);
397
if (static_cast<uint32>(id) != static_cast<uint32>(lp)) {
401
if (::SetForegroundWindow(hwnd) == 0) {
402
LOG(ERROR) << "::SetFOregroundWindow() failed";
408
void WinUtil::ActivateWindow(uint32 process_id) {
410
if (::EnumWindows(EnumWindowsProc, static_cast<LPARAM>(process_id)) != 0) {
411
LOG(ERROR) << "could not find the exsisting window.";